好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Angular Reactive Forms -- Model-Driven Forms响应式表单

Angular 4.x 中有两种表单:

Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )  官方文档:https://v2.angular.cn/docs/ts/latest/guide/forms.html

Reactive Forms (Model-Driven Forms) - 响应式表单  官方文档: https://v2.angular.cn/docs/ts/latest/guide/reactive-forms.html

Template-Driven Forms vs Reactive Forms

Template-Driven Forms (模板驱动表单) 的特点

使用方便

适用于简单的场景

通过 [(ngModel)] 实现数据双向绑定

最小化组件类的代码

不易于单元测试

异步的,模板驱动表单会委托指令来创建它们的表单控件。 为了消除“检查完后又变化了”的错误,这些指令需要消耗一个以上的变更检测周期来构建整个控件树。 这意味着在从组件类中操纵任何控件之前,我们都必须先等待一个节拍。

Reactive Forms (响应式表单) 的特点

比较灵活

适用于复杂的场景

简化了HTML模板的代码,把验证逻辑抽离到组件类中

方便的跟踪表单控件值的变化

易于单元测试

同步的 ,使用响应式表单,我们会在代码中创建整个表单控件树。 我们可以立即更新一个值或者深入到表单中的任意节点,因为所有的控件都始终是可用的。

使用 reactive forms 表单前,我们必须在  @NgModule  中导入  @angular/forms  库中的  ReactiveFormsModule :

import { ReactiveFormsModule }  from   '  @angular/forms  '  ;

@NgModule({
  imports: [
    ...,
    ReactiveFormsModule
  ],
  declarations: [...],
  bootstrap: [...]
})
export   class  AppModule {}

友情提示:若使用 reactive forms,则导入 ReactiveFormsModule;若使用 template-driven 表单,则导入 FormsModule。

FormControl 和 FormGroup

FormControl - 它是一个为单个表单控件提供支持的类,可用于跟踪控件的值和验证状态,此外还提供了一系列公共API。

使用示例:

 ngOnInit() {
    this .myControl =  new  FormControl( '  Semlinker  '  );
} 

FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。

使用示例:

 ngOnInit() {
    this .myGroup =  new   FormGroup({
    name:   new  FormControl( '  Semlinker  '  ),
    location:   new  FormControl( '  China, CN  '  )
  });
} 

现在我们已经创建了  FormControl  和  FormGroup  实例,接下来我们来看一下如何使用:

<form novalidate [formGroup]= "  myGroup  " > 
  Name:  <input type= "  text  "  formControlName= "  name  " > 
  Location:  <input type= "  text  "  formControlName= "  location  " >
</form>

注意事项:Template-Driven Forms 中介绍的  ngModel  和  name=""  属性,已经被移除了。这是一件好事,让我们的模板更简洁。

上面示例中,我们必须使用  [formGroup]  绑定我们创建的  myGroup  对象,除此之外还要使用  formControlName  指令,绑定我们创建的  FormControl  控件。此时的表单结构如下:

FormGroup ->  '  myGroup  '  
    FormControl  ->  '  name  '  
    FormControl  ->  '  location  ' 

User interface

signup.interface.ts

export  interface   User {
  name:   string  ;
  account: {
    email:   string  ;
    confirm:   string  ;
  }
} 

与之对应的表单结构如下:

FormGroup ->  '  user  '  
    FormControl  ->  '  name  '  
    FormGroup  ->  '  account  '  
        FormControl  ->  '  email  '  
        FormControl  ->  '  confirm  ' 

是的,我们可以创建嵌套的 FormGroup 集合!让我们更新一下组件 (不包含初始数据):

import { Component, OnInit }  from   '  @angular/core  '  ;
import { FormControl, FormGroup }   from   '  @angular/forms  '  ;

@Component({...})
export   class   SignupFormComponent implements OnInit {
  user: FormGroup;
  ngOnInit() {
      this .user =  new   FormGroup({
      name:   new  FormControl( ''  ),
      account:   new   FormGroup({
        email:   new  FormControl( ''  ),
        confirm:   new  FormControl( ''  )
      })
    });
  }
} 

如果我们想要设置初始数据,我们可以按照上述示例进行设置。通常情况下,我们通过服务端提供的 API 接口来获取表单的初始信息。

绑定FormGroup模型

<form novalidate [formGroup]= "  user  " >
  <label>
    <span>Full name</span>
    < input
      type = "  text  "  
      placeholder = "  Your full name  "  
      formControlName = "  name  " >
  </label>
  <div formGroupName= "  account  " >
    <label>
      <span>Email address</span>
      < input
        type = "  email  "  
        placeholder = "  Your email address  "  
        formControlName = "  email  " >
    </label>
    <label>
      <span>Confirm address</span>
      < input
        type = "  email  "  
        placeholder = "  Confirm your email address  "  
        formControlName = "  confirm  " >
    </label>
  </div>
  <button type= "  submit  " >Sign up</button>
</form>

现在  FormGroup  与  FormControl  对象与 DOM 结构的关联信息如下:

 //   JavaScript APIs 
FormGroup ->  '  user  '  
    FormControl  ->  '  name  '  
    FormGroup  ->  '  account  '  
        FormControl  ->  '  email  '  
        FormControl  ->  '  confirm  ' 

 //   DOM bindings 
formGroup ->  '  user  '  
    formControlName  ->  '  name  '  
    formGroupName  ->  '  account  '  
        formControlName  ->  '  email  '  
        formControlName  ->  '  confirm  ' 

当使用模板驱动的表单时,为了获取  f.value  表单的值,我们需要先执行  #f="ngForm"  的操作。而对于使用响应式的表单,我们可以通过以下方式,方便的获取表单的值:

{{ user.value | json }}  //   { name: '', account: { email: '', confirm: '' }} 

Reactive submit

跟模板驱动的表单一样,我们可以通过  ngSubmit  输出属性,处理表单的提交逻辑:

<form novalidate (ngSubmit)= "  onSubmit(user)  "  [formGroup]= "  user  " > 
  ...
 </form>

需要注意的是,我们使用  user  对象作为  onSubmit()  方法的参数,这使得我们可以获取表单对象的相关信息,具体处理逻辑如下:

export  class   SignupFormComponent {
  user: FormGroup;
  onSubmit({ value, valid }: { value: User, valid: boolean }) {
    console.log(value, valid);
  }
} 

上面代码中,我们使用  Object destructuring  (对象解构) 的方式,从 user  对象中获取  value  和  valid  属性的值。其中  value 的值,就是  user.value  的值。在实际应用中,我们是不需要传递  user  参数的:

export  class   SignupFormComponent {
  user: FormGroup;
  onSubmit() {
    console.log(  this .user.value,  this  .user.valid);
  }
} 

表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。

Reactive error validation

接下来我们来为表单添加验证规则,首先我们需要从  @angular/forms  中导入  Validators 。具体使用示例如下:

 ngOnInit() {
    this .user =  new   FormGroup({
    name:   new  FormControl( '' , [Validators.required, Validators.minLength( 2  )]),
    account:   new   FormGroup({
      email:   new  FormControl( ''  , Validators.required),
      confirm:   new  FormControl( ''  , Validators.required)
    })
  });
} 

通过以上示例,我们可以看出,如果表单控制包含多种验证规则,可以使用数组声明多种验证规则。若只包含一种验证规则,直接声明就好。通过这种方式,我们就不需要在模板的输入控件中添加  required  属性。接下来我们来添加表单验证失败时,不允许进行表单提交功能:

<form novalidate (ngSubmit)= "  onSubmit(user)  "  [formGroup]= "  user  " > 
  ...
   <button type= "  submit  "  [disabled]= "  user.invalid  " >Sign up</button>
</form>

那么问题来了,我们要如何获取表单控件的验证信息?我们可以使用模板驱动表单中介绍的方式,具体如下:

<form novalidate [formGroup]= "  user  " > 
  {{ user.controls.name ?.errors |  json }}
 </form>

友情提示:?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。

此外我们也可以使用  FormGroup  对象提供的 API,来获取表单控件验证的错误信息:

<form novalidate [formGroup]= "  user  " > 
  {{ user.  get ( '  name  ' ).errors |  json }}
 </form>

完整代码:

import { Component, OnInit }  from   '  @angular/core  '  ;
import { FormControl, FormGroup, Validators }   from   '  @angular/forms  '  ;
import { User }   from   '  ./signup.interface  '  ;
@Component({
  selector:   '  signup-form  '  ,
  template: `
     <form novalidate (ngSubmit)= "  onSubmit(user)  "  [formGroup]= "  user  " >
      <label>
        <span>Full name</span>
        <input type= "  text  "  placeholder= "  Your full name  "  formControlName= "  name  " >
      </label>
      <div  class = "  error  "  *ngIf= "  user.get('name').hasError('required') &&  
            user. get ( '  name  ' ).touched "  > 
        Name  is   required
       </div>
      <div  class = "  error  "  *ngIf= "  user.get('name').hasError('minlength') &&  
            user. get ( '  name  ' ).touched "  > 
        Minimum of  2   characters
       </div>
      <div formGroupName= "  account  " >
        <label>
          <span>Email address</span>
          <input type= "  email  "  placeholder= "  Your email address  "  formControlName= "  email  " >
        </label>
        < div
            class = "  error  " 
          *ngIf= "  user.get('account').get('email').hasError('required') &&  
             user. get ( '  account  ' ). get ( '  email  ' ).touched "  > 
                Email  is   required
         </div>
        <label>
          <span>Confirm address</span>
          <input type= "  email  "  placeholder= "  Confirm your email address  "   
             formControlName = "  confirm  " >
        </label>
        < div
            class = "  error  " 
          *ngIf= "  user.get('account').get('confirm').hasError('required') &&  
             user. get ( '  account  ' ). get ( '  confirm  ' ).touched "  > 
          Confirming email  is   required
         </div>
      </div>
      <button type= "  submit  "  [disabled]= "  user.invalid  " >Sign up</button>
    </form> 
  `
})
export   class   SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor() {}
  ngOnInit() {
      this .user =  new   FormGroup({
      name:   new  FormControl( '' , [Validators.required, Validators.minLength( 2  )]),
      account:   new   FormGroup({
        email:   new  FormControl( ''  , Validators.required),
        confirm:   new  FormControl( ''  , Validators.required)
      })
    });
  }
  onSubmit({ value, valid }: { value: User, valid: boolean }) {
    console.log(value, valid);
  }
} 

功能是实现了,但创建  FormGroup  对象的方式有点繁琐,Angular 团队也意识到这点,因此为我们提供  FormBuilder  ,来简化上面的操作。

Simplifying with FormBuilder

首先我们需要从  @angular/forms  中导入  FormBuilder :

import { FormBuilder, FormGroup, Validators }  from   '  @angular/forms  '  ;

export   class   SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor(  private   fb: FormBuilder) {}
  ...
} 

使用FormBuilder:

 @Component({...})
export   class   SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor(  private   fb: FormBuilder) {}
  ngOnInit() {
      this .user =  this  .fb.group({
      name: [  '' , [Validators.required, Validators.minLength( 2  )]],
      account:   this  .fb.group({
        email: [  ''  , Validators.required],
        confirm: [  ''  , Validators.required]
      })
    });
  }
  onSubmit({ value, valid }: { value: User, valid: boolean }) {
    console.log(value, valid);
  }
} 

 

转自:https://segmentfault.com/a/1190000009041192

查看更多关于Angular Reactive Forms -- Model-Driven Forms响应式表单的详细内容...

  阅读:36次