在獨立控件模式下使用 ngModel在表單中使用 ngModel在表單組中使用獨立 ngModel經過選項設置 ngModel 的 name 屬性總結參考文獻javascript
NgModel 指令使用場景比較多,還會和 NgForm 結合使用,因此很是有必要單獨寫一篇學習筆記。html
NgModel 根據領域對象建立一個 FormControl
實例,並把它綁定到一個表單控件元素上。java
官方說明:git
這個
FormControl
實例將會跟蹤值、用戶交互和控件的驗證狀態,以保持視圖與模型的同步。 若是用在某個父表單中,該指令還會把本身註冊爲這個父表單的子控件。github這個指令能夠單獨使用,也能夠用做一個大表單的一部分。你所要作的一切就是用
ngModel
選擇器來激活它。web它能夠接受一個領域模型做爲可選的
Input
。若是使用[]
語法來單向綁定到ngModel
,那麼在組件類中修改領域模型將會更新視圖中的值。 若是使用[()]
語法來雙向綁定到ngModel
,那麼視圖中值的變化會隨時同步回組件類中的領域模型。 json
若是你但願查看與 FormControl
相關的屬性(好比校驗狀態),你也可使用 ngModel
做爲鍵,把該指令導出到一個局部模板變量中(如:#myVar="ngModel"
)。 你也可使用該指令的 control
屬性來訪問此控件,實際上你要用到的大多數屬性(如 valid
和 dirty
)都會委託給該控件,這樣你就能夠直接訪問這些屬性了。 你能夠在 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
當在 <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"的」「標籤時,系統會自動爲這個標籤建立一個叫作」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 }} 索引到該對象的值了。
複製代碼
在講解該案例前,須要建立一個自定義表單控件,這裏我直接將相關代碼列舉出來,具體講解我會在參考文獻處標註。
首先須要建立一個組件 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),
multi: true
}]
})
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>
<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接口