Angular使用總結 --- 模型驅動表單


模型驅動表單

  之前有篇博文總結了 模版驅動表單 , 以及 模版驅動表單的自定義校驗 , 本篇總結下模型驅動表單。

  與模版驅動表單是不同的編程思路,偏向於數據模型。先在組件中建立表單控件的對象樹,再綁定到組件模版的原生表單控件上。而模版驅動表單則是在組件模版中使用了內置的 ngForm、ngModel指令,這些指令會自動完成很多工作,以達到雙向綁定、監聽form和表單控件的狀態等等 的目的。雖然模版驅動表單寫起來更見的簡潔方便,因為指令自動完成了很多工作,但是也正式由於委托指令,所以會導致異步的問題。官網描述 如下:

響應式表單是同步的而模板驅動表單是異步的。

使用響應式表單,你會在代碼中創建整個表單控件樹。 你可以立即更新一個值或者深入到表單中的任意節點,因為所有的控件都始終是可用的。

模板驅動表單會委托指令來創建它們的表單控件。 為了消除“檢查完后又變化了”的錯誤,這些指令需要消耗一個以上的變更檢測周期來構建整個控件樹。 這意味着在從組件類中操縱任何控件之前,你都必須先等待一個節拍。

  雖然目前本人目前使用模版驅動表單,還沒有遇到因異步導致的問題,但是也許在某一天,bug會從天而降。

  模型驅動表單需要引入模塊 : ReactiveFormsModule  。而模版驅動表單需要引入 FormsModule

  Angular提供了一些方法來構建表單控件的對象樹。FormControlFormGroupFormArray是構建表單模型的三種表單類,它們有共同的基類 AbstractControl。這三種表單類有不同的作用。

FormControl

  FormControl用來構建一個單獨的表單控件的值和狀態,它會對應這模型中的一個表單元素。FormControl類的構造函數如下:

constructor(formState?: any, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null);

  接受三個可選參數:初始值、驗證器、異步驗證器。簡單的創建一個formControl

//引入依賴
import { FormControl , Validators} from '@angular/forms';


//創建對象  設置初始值,和校驗規則
public name = new FormControl('' , Validators.required);

  使用 formControl 可將此控件對象綁定到組件模板中

<input class="form-control" type="text"  id="login-name" placeholder="請輸入登錄帳號" [formControl]="name">
 <div class="form-group">
  {{name.value}} || {{name.status}}
 </div>

  隨便輸入內容,已經能正確監聽控件的值和狀態了!每當輸入框內容中有變化,name(控件對象)都會隨之改變。

  此時都是輸入內容變化都會觸發控件對象更新,那么如何修改觸發更新的時機呢?在FormControl的構造函數中有 AbstractControlOptions ,其中的 updateOn 配置項可以修改觸發時機。從源碼中可以查看到有三種不同的方式, 值變化,失焦,提交。

// 失焦時觸發更新  
public name = new FormControl('' , {
    validators : Validators.required,
    updateOn : 'blur'
  });

FormGroup

  FormGroup對象用來跟蹤一組 AbstractControl 的值和狀態,即可以跟蹤多個formControl和FormGroup。構造函數如下:

    constructor(controls: {
        [key: string]: AbstractControl;
    }, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null);

  創建一個簡單的formGroup,並綁定到模版中

 // 簡單的formGroup  
public loginForm = new FormGroup({
    name : new FormControl('', Validators.maxLength(20)),
    psw : new FormControl('')
  }, Validators.required);

// 綁定到模版中
  <form class="login-area" (submit)="login()" [formGroup] = "loginForm">
    <div class="form-group">
        <input class="form-control" type="text"  id="login-name" placeholder="請輸入登錄帳號" formControlName = 'name'>
    </div>
    <div class="form-group">
      <input class="form-control" type="password"  id="login-pwd" placeholder="請輸入登錄密碼" formControlName = 'psw'>
    </div>
    <div class="form-group">
      {{loginForm.value | json}}
    </div>
  </form>

  這里就不能再使用[formControl]來綁定表單控件了,因為這里創建的是FormGroup,已經通過[formGroup]綁定到了 form上了,這你需要通過FormGroup來找到FormControl,在表單控件中用formControlName來指定對應的FormControl對象。

FormBuilder

  以上的方式構建表單對象樹太過於繁瑣,需要使用很多的new FormGroup() (構建多級表單的時候) 和 new FromControl()  ,FormBuilder就是簡化構造方式的的服務。FormBuilder服務中有三個工廠函數 group()  control() array() 可以簡化代碼量。使用FormBuilder的方式再次構建上面的表單對象樹。

  public loginForm: FormGroup;

  constructor(
    private fb: FormBuilder
  ) {
  this.loginForm = fb.group({
      name : ['' , Validators.required],
      psw : ['']
    }, {
      validator : Validators.minLength(2)
    });
  }

  已經減少了一些代碼量,在表單很龐大的時候,對比會特別明顯。注意group工具函數指定表單組的校驗規則時與原new GroupForm()參數放置的位置不一樣。

    /**
     * Construct a new {@link FormGroup} with the given map of configuration.
     * Valid keys for the `extra` parameter map are `validator` and `asyncValidator`.
     *
     * See the {@link FormGroup} constructor for more details.
     */
    group(controlsConfig: {
        [key: string]: any;
    }, extra?: { [key: string]: any; } | null): FormGroup;

 

多級FormGroup

  為了更高效的管理表單,可以把類似的表單放在一起,形成一個FormGroup,這樣就可以同時管理這些類似表單的值和狀態。這樣創建就會產生多級FormGroup。再通過formGroupName把子group導向模版中。

    this.loginForm = fb.group({
 name: this.fb.group({ firstname: ['' , Validators.required], lastname: '' }),
      psw: ['' , Validators.required]
    });
  
  <div formGroupName="name"> 
        <div class="form-group">
            <input class="form-control" type="text"  id="login-name" placeholder="請輸入姓" formControlName = 'firstname'>
        </div>
        <div class="form-group">
            <input class="form-control" type="text"  id="login-name" placeholder="請輸入名" formControlName = 'lastname'>
        </div>
    </div>

 

獲取控件狀態

  在FormGroup中可以通過get()方法定位到表單控件對象,然后就能夠獲取到各種狀態了,和之前模版驅動表單中介紹的一樣。可以簡單的封裝一個獲取函數,這樣在模版中就能非常方便的獲取到各個formControl。

 getFormControl(name: string) { return this.loginForm.get(name); } 

   <div class="form-group">
        {{getFormControl('name').status}}
    </div>
    <div class="form-group">
        {{getFormControl('name.firstname').status}}
    </div>

 

自定義校驗

  模型驅動表單的自定義校驗方便很多,因為可以直接自定義各級formControl。以上面的多級group為例,添加定義的校驗規則:firstname和lastname不能輸入相同的值。

  sameName (): ValidatorFn {
    return (control: AbstractControl): { [key: string]: any} => { // control指向使用它的formControl或者formGroup
      return control.get('firstname').value === control.get('lastname').value ? { 'sameName' : true } : null; // 子formcontrol的值相同就返回錯誤
    };
  }

  在模型中使用此校驗規則

    this.loginForm = fb.group({
      name: this.fb.group({
        firstname: ['' , Validators.required],
        lastname: ['']
      } , { validator : [ // 對整個name的校驗規則 this.sameName(), Validators.minLength(2) ] }),
      psw: ['' , Validators.required]
    });

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM