NgModel指令學習筆記

在獨立控件模式下使用 ngModel在表單中使用 ngModel在表單組中使用獨立 ngModel經過選項設置 ngModel 的 name 屬性總結參考文獻javascript

NgModel 指令使用場景比較多,還會和 NgForm 結合使用,因此很是有必要單獨寫一篇學習筆記。html

NgModel 根據領域對象建立一個 FormControl 實例,並把它綁定到一個表單控件元素上。java

官方說明:git

這個 FormControl 實例將會跟蹤值、用戶交互和控件的驗證狀態,以保持視圖與模型的同步。 若是用在某個父表單中,該指令還會把本身註冊爲這個父表單的子控件。github

這個指令能夠單獨使用,也能夠用做一個大表單的一部分。你所要作的一切就是用 ngModel 選擇器來激活它。web

它能夠接受一個領域模型做爲可選的 Input。若是使用 [] 語法來單向綁定到 ngModel,那麼在組件類中修改領域模型將會更新視圖中的值。 若是使用 [()] 語法來雙向綁定到 ngModel,那麼視圖中值的變化會隨時同步回組件類中的領域模型。 json

在獨立控件模式下使用 ngModel

若是你但願查看與 FormControl 相關的屬性(好比校驗狀態),你也可使用 ngModel 做爲鍵,把該指令導出到一個局部模板變量中(如:#myVar="ngModel")。 你也可使用該指令的 control 屬性來訪問此控件,實際上你要用到的大多數屬性(如 validdirty)都會委託給該控件,這樣你就能夠直接訪問這些屬性了。 你能夠在 AbstractControlDirective 中直接查看這些屬性的完整列表。 以下所示:segmentfault

abstract class AbstractControlDirective {
  abstract control: AbstractControl | null
  value: any
  valid: boolean | null
  invalid: boolean | null
  pending: boolean | null
  disabled: boolean | null
  enabled: boolean | null
  errors: ValidationErrors | null
  pristine: boolean | null
  dirty: boolean | null
  touched: boolean | null
  status: string | null
  untouched: boolean | null
  statusChanges: Observable<any> | null
  valueChanges: Observable<any> | null
  path: string[] | null
  reset(value: any = undefined): void
  hasError(errorCode: string, path?: string | (string | number)[]): boolean
  getError(errorCode: string, path?: string | (string | number)[]): any
}
複製代碼

下面是一個在簡單的獨立控件中使用 ngModel 的例子:api

import {Component} from '@angular/core';

@Component({
  selector'example-app',
  template`
    <input [(ngModel)]="name" #ctrl="ngModel" required>

    <p>Value: {{ name }}</p>
    <p>Value: {{ ctrl.value }}</p>
    <p>Valid: {{ ctrl.valid }}</p>

    <button (click)="setValue()">Set value</button>
  `
,
})
export class SimpleNgModelComp {
  name: string = '';

  setValue() { this.name = 'Nancy'; }
}
複製代碼

頁面測試:app

在表單中使用 ngModel

當在 <form> 標籤中使用 ngModel 時,你還須要提供一個 name 屬性,以便該控件可使用這個名字把本身註冊到父表單中。

在父表單的上下文中,一般不用包含單向或雙向綁定,由於這個父表單將會爲你同步該值。 你可使用 ngForm 把它導出給一個模板局部變量(如 #f="ngForm"),以訪問它的屬性。 能夠在任何須要提交表單的地方使用它。

若是你只是要爲表單設置初始值,對 ngModel 使用單向綁定就夠了。在提交時,你可使用從表單導出的值,而沒必要使用領域模型的值。

下面的例子展現瞭如何在表單中使用 ngModel

import {Component} from '@angular/core';
import {NgForm} from '@angular/forms';

@Component({
  selector'example-app',
  template`
    <h2>ngForm中使用 ngModel</h2>
    <div>
      <form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
        <input name="first" ngModel required #first="ngModel">
        <br>
        <input name="last" ngModel>
        <br>
        <button>Submit</button>
      </form>

      <p>First name value: {{ first.value }}</p>
      <p>First name valid: {{ first.valid }}</p>
      <p>Form value: {{ f.value | json }}</p>
      <p>Form valid: {{ f.valid }}</p>

      <div [hidden]="!f.valid">
        <p>{{submitMessage }}</p>
      </div>
    </div>
  `
,
})
export class SimpleFormComp {
   submitMessage = '';
  onSubmit(f: NgForm) {
    console.log(f.value);  // { first: '', last: '' }
    console.log(f.valid);  // false
    this.submitMessage = '數據已提交';
  }
}
複製代碼

注意:單獨 ngModel 的做用是通知 ngForm.value,我要向你那裏加入一個 property,其 key 值是組件的 name屬性值,其 value 爲空字符串。 因此若是沒有爲 ngModel 賦值的話,則必須存在 name 屬性。

頁面測試:

在表單組中使用獨立 ngModel

使用帶有「ngModel"的」「標籤時,系統會自動爲這個標籤建立一個叫作」FormControl"的對象,而且會自動把它添加到」FormGroup"中。而「FormControl"在」FomGroup「中是用""標籤上的」name"屬性來作標識的。

<form #f="ngForm">
  <input type="text" ngModel name="firstField">
  <span>{{ f.controls['firstField']?.value }}</span>
</form>
複製代碼

若是沒有使用「name」這個屬性,那麼將會報錯:

Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
複製代碼

解決方法除了把「name」屬性添加上外,還有第二種選擇,就是給""標籤設置一個 ngModelOptions。以下:

<form #f="ngForm">
  <input type="text" ngModel [ngModelOptions]="{standalone: true}">
  <span>{{ f.controls['firstField']?.value }}</span>
</form>
複製代碼

當設置了這個屬性,的 FormControl 對象就不會添加到FormGroup內,也就不能經過

{{ f.controls['firstField']?.value }} 索引到該對象的值了。
複製代碼

經過選項設置 ngModel 的 name 屬性

在講解該案例前,須要建立一個自定義表單控件,這裏我直接將相關代碼列舉出來,具體講解我會在參考文獻處標註。

首先須要建立一個組件 formcontrol,修改 formcontrol.component.ts:

import { Component, Input, forwardRef } from '@angular/core';
import {
  ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS,
  AbstractControl, ValidatorFn, ValidationErrors, FormControl
from '@angular/forms';

@Component({
  selector'form-control',
  templateUrl'./formcontrol.component.html',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => FormcontrolComponent),
    multitrue
  }]
})
export class FormcontrolComponent implements ControlValueAccessor {

  @Input() _count: number = 0;

  propagateOnChange: (value: any) => void = (_: any) => { };
  propagateOnTouched: (value: any) => void = (_: any) => { };

  ngOnInit() { }

  get count() {
    return this._count;
  }

  set count(value: number) {
    this._count = value;
    this.propagateOnChange(this._count);
  }

  writeValue(value: any) {
    if (value) {
      this.count = value;
    }
  }

  registerOnChange(fn: any) {
    this.propagateOnChange = fn;
  }

  registerOnTouched(fn: any) {
    this.propagateOnTouched = fn;
  }

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}
複製代碼

對應的 formcontrol.component.html :

<div>
  <p>當前值: {{ count }}</p>
  <button (click)="increment()"> + </button>&nbsp;&nbsp;
  <button (click)="decrement()"> - </button>
</div>
複製代碼

而後在 simple-form.component.html 中使用自定義控件。

<form #form="ngForm">
  <form-control name="counter" ngModel></form-control>
  <button type="submit">Submit</button>
  <br>
  <span>counter value: {{ form.controls['counter']?.value }}</span>
</form>
複製代碼

頁面測試:

上述代碼是自定義表單控件的實現方式,基於該案例驗證 ngModel 的另一種使用場景。

下面的例子展現了設置 name 屬性的另外一種方式。該 name 屬性要和自定義表單組件一塊兒使用,而該自定義組件的 @Input 屬性 name 已用做其它用途。

<form #form="ngForm">
  <form-control name="counter" ngModel [ngModelOptions]="{name: 'counter2'}"></form-control>
  <button type="submit">Submit</button>
  <br>
  <span>counter value: {{ form.controls['counter']?.value }}</span>
  <br>
  <span>counter2 value: {{ form.controls['user']?.value }}</span>
</form>
複製代碼

頁面測試:

總結

關於 NgModel 的使用場景不少,尤爲會結合 NgForm 指令使用,因此搞清楚每一個場景下的含義尤其重要,對於本身編寫 Angular 代碼有很大的幫助。以上內容爲我的參考官方文檔作的學習筆記,若有錯誤,望不吝賜教。

參考文獻

Angular學習筆記 ——input 標籤上的【name屬性】和【ngModelOptions屬性】

官方文檔

自定義表單控件 ControlValueAccessor接口

Angular ControlValueAccessor - 自定義表單控件介紹與實戰

angular自定義表單控件(轉)

Angular 自定義表單控件 -- CheckboxGroupComponent

相關文章
相關標籤/搜索