Angular支援template-drive form和reactive form,但是,signal容易與template-driven form配合使用。在NgModel
能夠與signa
l執行two-way binding之後,我更多地使用template-driven form。
今天,我想講一下NgModel
的故事。 Angular團隊最初引入它是為了綁定到primitve或物件屬性 (object property)。現在,ngModel
可以綁定到signals
來讀取和寫回它們。
NgModel
可透過將FormsModule
匯入獨立元件 (standalone component) 來使用。
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [FormsModule],
template: ‘’,
})
export class App {}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [FormsModule],
template: `
<div>
<p class="bold">NgModel a primitive</p>
<p>
<span>Num:</span><input type="number" [(ngModel)]="num" />
</p>
<p>
<span>Num Property:</span><input type="number" [(ngModel)]="obj.num" />
</p>
<span>Output: </span><span>{{ num }}, {{ num + 2 }}, {{ obj.num }}, {{ obj.num + 2 }}</span>
<hr />
</div>
`,
})
export class App {
num = 1;
obj = { num: 1 };
}
當我在2016年開始使用Angular 2時,開發人員只能分別將primitive和物件屬性 (object property)綁定到ngModel
。在範例中,數字和數字類型的物件屬性是與ngModel
的雙向綁定。 對於剛接觸Angular
的人來說,[(ngModel]]="num"
是雙向綁定 (two-way binding)ngModel
的語法。當輸入欄位發生變化時,對應的變數也會改變。範本顯示最新值:num
,num + 2
,obj.num
和obj.num + 2
。
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { BehaviorSubject, map } from 'rxjs';
@Component({
selector: 'app-root',
standalone: true,
imports: [FormsModule, AsyncPipe],
template: `
<div>
<p class="bold">NgModel a Behavior Subject</p>
<p>
<span>Num:</span><input type="number" [ngModel]="numSub.getValue()" (ngModelChange)="numSub.next($event)" />
</p>
<span>Output: </span><span>{{ numSub.getValue() }}, {{ plus2$ | async }}</span>
<hr />
</div>
`,
})
export class App {
// bind a behaviorSubject to ngModel
numSub = new BehaviorSubject(1);
// create a new Observable that adds 2 to the new num
plus2$ = this.numSub.pipe(map((x) => x + 2));
}
一段時間後,開發了一種反應式模式 (reactive pattern) 來將BehaviorSubject
綁定到ngModel
。當BehaviorSubject
獲得新的表單值時,訂閱該subject
的消費者將獲取該值並可以操縱它來派生新結果。 在範例中,輸入欄位顯示 1,因為numSub
的初始值是透過[ngModel]=numSub
傳遞的。當輸入欄位更新時, (ngModelChange)=numSub.next($even)
會將數值傳送給subject
。 plus2$
Observable然後將值對應到value + 2
。 最後,該組件匯入AsyncPipe
並解析 (resolve) 範本中的plus2$
。
在Angular引入signal
之後,我用signal
和computed signal
重寫了BehaviorSubject
範例。 正是因為
RxJS
最適合非同步活動和串流Signa
最適合同步活動,更新輸入欄位是同步操作。signal
的程式碼更改:
之前使用 RxJS
numSub = new BehaviorSubject(1);
plus2$ = this.numSub.pipe(map((v) => v + 2));
有signal後
// bind a signal to ngModel
numBeforeV17 = signal(1);
// derive a new read-only signal to show num + 2
plus2BeforeV17 = computed(() => this.numBeforeV17() + 2);
之前使用 RxJS
<p>
<span>Num:</span><input type="number" [ngModel]="numSub.getValue()" (ngModelChange)="numSub.next($event)" />
</p>
<span>Output: </span><span>{{ numSub.getValue() }}, {{ plus2$ | async }}</span>
有signal後
<p>
<span>Num:</span><input type="number" [ngModel]="numBeforeV17()" (ngModelChange)="numBeforeV17.set($event)" />
</p>
<span>Output: </span><span>{{ numBeforeV17() }}, {{ plus2BeforeV17() }}</span>
此外,我可以從遷移過程中刪除AsynPipe
。 signal
版本看起來與RxJS
版本類似,更改的動機可能不高。 我希望您在看到NgModel
與signal
的雙向綁定 (two-way binding) 後改變主意。
在Angular 17.0.1中,ngModel
終於支援signal
了。 [(ngModel)]="signal name"
是可行的,並且減少了範本中的樣板程式碼
Angular版本17.0.1之前
// bind a signal to ngModel
numBeforeV17 = signal(1);
// derive a new read-only signal to show num + 2
plus2BeforeV17 = computed(() => this.numBeforeV17() + 2);
Angular版本17.0.1之後
// bind a signal to ngModel
numV17 = signal(1);
// derive a new read-only signal to show num + 2
plus2V17 = computed(() => this.numV17() + 2);
Angular版本17.0.1之前
<p>
<span>Num:</span><input type="number" [ngModel]="numBeforeV17()" (ngModelChange)="numBeforeV17.set($event)" />
</p>
<span>Output: </span><span>{{ numBeforeV17() }}, {{ plus2BeforeV17() }}</span>
Angular版本17.0.1之後
<p>
<span>Num:</span><input type="number" [(ngModel)]="numV17()" />
</p>
<span>Output: </span><span>{{ numV17() }}, {{ plus2V17() }}</span>
程式碼變更產生相同的結果,並且減少了ngModel
的樣板程式碼。對我來說,我更喜歡將signal
綁定到 ngModel
,它會在更改時通知其他signals
和computed signals
。
NgModel
促進signal與template-driven form的整合。NgModel
的不同技術
[(ngModel)]
語法將primitive或Object property綁定到表單控制元素。BehaviorSubject
來追蹤HTML輸入值。 [ngModel]
綁定到behaviorSubject.getValue()
,並且(ngModelChange)
將值傳送至behaviorSubject.next()
。signal
分別綁定到[ngMode]
和(ngModelChange)
。signal
可以按以下語法綁定到ngModel
:[(ngModel)]="<signal name>"
。鐵人三賽第9天到此結束。
Stackblitz Demo: