Angular 2 ngModel 的內幕(?)

關於 NgModel

這篇文章主要來深入討論 [(ngModel)],若你已經有在開發 Angular 2 的專案,或是自己嘗試練習 Angular 的過程中, [(ngModel)] 一定會接觸到的語法,透過[(ngModel)] 實現雙向繫結的功能,但,為什麼要使用中括號和小括號呢?

其實,繫結語法中的 [( )] 符號是一個很好的提示。

在屬性繫結中,一個值從 Model 中傳到螢幕上的目標屬性。我們把名稱放在中括號 [ ] 裡面來標記出目標屬性 。這是一個 從 Model 到 View 的單向資料繫結。

在事件繫結中,從螢幕上的目標屬性把值傳到 Model 裡。我們透過小括號 ( ) 把名稱包起來標記出目標屬性。這是一個 從 View 到 Model 的反向單向資料繫結 。

由此可知,Angular 選擇了組合符號 [( )] 來標記出雙向資料繫結和雙向資料流。

事實上,我們可以把 NgModel 繫結拆成兩個獨立的繫結,就像上一篇我們重寫的 「Name」 <input>  繫結一樣:

./app/hero-form/hero-form.component.html
1
2
3
4
<input type="text" class="form-control" required
[ngModel]="model.name"
(ngModelChange)="model.name = $event" >
TODO: remove this: {{model.name}}

這個屬性繫結看起來很眼熟,但事件繫結看起來有點怪。

ngModelChange 並不是 <input> 元素的事件。 它實際上是一個來自 ngModel directive 的事件屬性。 當 Angular 在表單中看到一個 [(x)] 的繫結目標時, 它會預計這個 x directive 有一個名為 x 的輸入屬性,和一個名為 xChange 的輸出屬性。

樣板表達式 ( template expression ) 中的另一個奇怪的地方是 model.name = $event 。 我們以前看到的 $event 變數是來自 DOM 事件的。 但 ngModelChange 屬性不會產生 DOM 事件,它是一個 Angular 的 EventEmitter 類型的屬性,當它觸發時, return 的是輸入框的值 - 這個值剛好和我們必須設給 Model 的 name 屬性一樣。

很高興知道這些,但這樣實際嗎?實務上我們幾乎總是較習慣使用 [(ngModel)] 形式的雙向繫結。 只有當我們不得不在事件處理函式中做一些特別的事情 (例如合併或限制按鍵頻率) 時,才需要另外拆出獨立的事件處理函式。

想了解更多關於 ngModel 和其它樣板語法的內容,請參考 Template Syntax  章節。