成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

@angular/forms 源碼解析之雙向綁定

yangrd / 659人閱讀

摘要:由于的屬性提供了令牌,并且該令牌指向的對(duì)象就是,所以構(gòu)造函數(shù)中注入的令牌包含的對(duì)象數(shù)組只有一個(gè)。這樣的構(gòu)造函數(shù)里就會(huì)包含一個(gè)對(duì)象,然后把這個(gè)傳給對(duì)象,最后注冊(cè)回調(diào),這樣以后值更新時(shí)就會(huì)運(yùn)行。整個(gè)包的設(shè)計(jì)也是按照這種數(shù)據(jù)流形式,并不復(fù)雜。

我們知道,Angular 的 @angular/forms 包提供了 NgModel 指令,來(lái)實(shí)現(xiàn)雙向綁定,即把一個(gè) JS 變量(假設(shè)為 name)與一個(gè) DOM 元素(假設(shè)為 input 元素)進(jìn)行綁定,這樣 name 的值發(fā)生變化,input 元素 的 value 也會(huì)自動(dòng)變化;input 元素的 value 發(fā)生變化,name 的值也會(huì)自動(dòng)變化。如下代碼,展示一個(gè)最簡(jiǎn)單的雙向綁定(也可見 stackblitz demo):

@Component({
  selector: "my-app",
  template: `
    
    
    

{{name}}

`, styleUrls: [ "./app.component.css" ] }) export class AppComponent { name = "banana"; }

上面代碼使用了 NgModel 指令來(lái)把變量 nameinput DOM 元素雙向綁定到了一起,這里為了更清晰理解 NgModel 的本質(zhì),沒(méi)有使用 [(ngModel)] 語(yǔ)法糖。實(shí)際上,在模板里寫 [(xxx)] 這種 "BANANA_BOX" 語(yǔ)法,@angular/compiler 的 Template Parser 會(huì)把這種語(yǔ)法拆解為為 [xxx](xxxChange),可看 L448-L453L501-L505,所以 [(xxx)] 僅僅是為了省事的簡(jiǎn)單寫法。

查看 stackblitz demo 可以看到,如果修改 input 里的值,name 變量的值也自動(dòng)發(fā)生變化了,這點(diǎn)可從與 name 綁定的 p 標(biāo)簽值自動(dòng)變化看出;如果點(diǎn)擊 button 修改了 name 的值,input 輸入框內(nèi)的 value 值也發(fā)生變化了,這點(diǎn)可從 input 框內(nèi)的值變化可看到。那 NgModel 指令是如何做到雙向綁定的呢?

在理解 NgModel 指令雙向綁定原理之前,可以先看看雙向綁定最簡(jiǎn)單形式:



Hello {{country}}!

點(diǎn)擊 button 修改 model 時(shí),就會(huì)自動(dòng)修改 input 的 value 值,即自動(dòng)修改 view,數(shù)據(jù)流方向就是 model -> view;更新 input 框內(nèi)值時(shí),就會(huì)自動(dòng)修改 country 這個(gè) model 值,這點(diǎn)可從綁定的 p 標(biāo)簽看到,這時(shí)數(shù)據(jù)流方向就是 view -> model。當(dāng)然,這是最簡(jiǎn)單且最不可擴(kuò)展的一個(gè)雙向綁定實(shí)例,如果去設(shè)計(jì)一個(gè)指令,不僅僅需要考慮 view 的不同類型,而且還需要考慮數(shù)據(jù)校驗(yàn)問(wèn)題。盡管如此,這個(gè)簡(jiǎn)單實(shí)例與 NgModel 指令本質(zhì)是類似的。

如果自己設(shè)計(jì)這樣一個(gè)雙向綁定指令,那它的輸入必然是綁定的變量 name,該指令接收 name 后再去更新 input 元素的 value 值(還得支持 textarea,select 等 DOM 元素,甚至組件等自定義 DOM 元素),這樣 name 發(fā)生變化,input 的 value 也會(huì)自動(dòng)變化,即 model -> view;輸出的必然是 input 元素的 value 值,然后賦值給 name,這樣 input 元素的值變化,name 值也自動(dòng)變化,即 view -> model。這里的最難點(diǎn)是該指令得能夠?qū)?DOM 元素(不管原生或者自定義 DOM 元素)的值,并且能夠監(jiān)聽 DOM 元素的值變化,讀取變化的值。 所以,為了支持原生 DOM 元素或自定義 DOM 元素,為了有個(gè)好的設(shè)計(jì)模式,必然會(huì)抽象出一個(gè)接口,來(lái)幫助指令去寫入和監(jiān)聽讀取 DOM 元素值,有了這個(gè)接口,事情就簡(jiǎn)單很多了。

現(xiàn)在,我們需要搞明白兩個(gè)問(wèn)題:name 值發(fā)生變化時(shí),input 的 value 如何自動(dòng)變化;input 的 value 變化,name 值如何自動(dòng)變化?

綁定到 input 上的 NgModel 指令在實(shí)例化時(shí),其 構(gòu)造函數(shù) 會(huì)首先查找出 ControlValueAccessor 對(duì)象,這個(gè) ControlValueAccessor 就是上文提到的抽象出來(lái)的對(duì)象,該對(duì)象會(huì)具體負(fù)責(zé)更新和監(jiān)聽讀取 DOM 元素的值。上文模板中的 input 元素不僅僅綁定了 NgModel 指令,實(shí)際上還綁定了 DefaultValueAccessor 指令,這點(diǎn)可以從該指令的選擇器知道,如果 input 模板是這么寫的:

那不僅僅綁定了 DefaultValueAccessor 指令,還綁定了 NumberValueAccessor 指令。

由于 DefaultValueAccessor 的 providers 屬性提供了 NG_VALUE_ACCESSOR 令牌,并且該令牌指向的對(duì)象就是 DefaultValueAccessor,所以 NgModel 構(gòu)造函數(shù)中注入的 NG_VALUE_ACCESSOR 令牌包含的 ControlValueAccessor 對(duì)象數(shù)組只有 DefaultValueAccessor 一個(gè)。如果是 type="number" 的 input,則 valueAccessors 包含 NumberValueAccessorDefaultValueAccessor 這兩個(gè)對(duì)象。構(gòu)造函數(shù)中的 selectValueAccessor() 方法會(huì)依次遍歷 NG_VALUE_ACCESSOR 令牌提供的 ControlValueAccessor 對(duì)象數(shù)組,如果是自定義的 ControlValueAccessor 優(yōu)先選擇自定義的,如果是 @angular/forms 內(nèi)置的 ControlValueAccessor 就選擇內(nèi)置的(內(nèi)置的也就 6 個(gè)),否則最后選擇默認(rèn)的 ControlValueAccessorDefaultValueAccessor 對(duì)象。對(duì)于本文 demo,那就是默認(rèn)的 DefaultValueAccessor 對(duì)象。注意的一點(diǎn)是,注入的 NG_VALUE_ACCESSOR 令牌有裝飾器 @Self,所以只能從自身去查找這個(gè)依賴,自身的意思是 NgModel 指令自己,和它一起掛載到 input 元素的其他指令。另外,input 上沒(méi)有綁定任何 validators 指令,所以注入的 NG_VALIDATORS 和 NG_ASYNC_VALIDATORS 令牌解析的值為空,并且 input 多帶帶使用,沒(méi)有放在 form 元素內(nèi),或 FormGroup 綁定的元素內(nèi),所以不存在宿主控件容器 ControlContainer,即 parent 也為空。

NgModel 指令在首次實(shí)例化時(shí),運(yùn)行 _setUpControl() 方法,利用 ControlValueAccessor(本 demo 即 DefaultValueAccessor 對(duì)象) 把 NgModel 指令內(nèi)部的 FormControl 對(duì)象與 DOM 元素綁定。由于本 demo 中,NgModel 指令綁定的 input 沒(méi)有父控件容器,所以會(huì)調(diào)用 _setUpStandalone 方法,核心方法就是 setUpControl(),該方法主要包含兩點(diǎn):第一點(diǎn),通過(guò)調(diào)用 setUpViewChangePipeline()DefaultValueAccessor 對(duì)象內(nèi)注冊(cè)一個(gè)回調(diào)函數(shù),這樣當(dāng) input 值發(fā)生變化時(shí),就觸發(fā) input 事件 時(shí),會(huì)執(zhí)行這個(gè)回調(diào)函數(shù),而這個(gè)回調(diào)函數(shù)的邏輯 一是更新 FormControl 的 value,二是讓 NgModel 指令拋出 ngModelChange 事件,該事件包含的值就是當(dāng)前 input 變化的新值,所以,setUpViewChangePipeline() 方法的作用就是搭建了 view -> model 的管道,這樣 view (這里是 input) 值發(fā)生變化時(shí),會(huì)同步 FormControl 對(duì)象的 value 值,并讓 NgModel 指令把這個(gè)新值輸出出去;第二點(diǎn),通過(guò)調(diào)用 setUpModelChangePipeline 方法向 FormControl 對(duì)象內(nèi)注冊(cè) 一個(gè)回調(diào),這個(gè)回調(diào)邏輯是當(dāng) FormControl 的 value 值發(fā)生變化時(shí)(本 demo 中就是 [ngModel]="name" 時(shí),name 值發(fā)生變化,也就是屬性值改變,這樣 isPropertyUpdated(changes, this.viewModel) 就為 true,這樣就會(huì)需要更新 FormControl 的 value 值 FormControl.setValue(value),從而會(huì) 觸發(fā) 上文說(shuō)的 FormControl 對(duì)象內(nèi)的回調(diào)函數(shù)),通過(guò)調(diào)用 ControlValueAccessor.writeValue() 方法去修改 view (這里是 input) 的 value 值(本 demo 中使用的是 DefaultValueAccessor.writeValue(value)),然后讓 NgModel 指令拋出 ngModelChange 事件,該事件包含的值就是當(dāng)前 FormControl 對(duì)象 變化的新值,所以,setUpModelChangePipeline() 方法的作用就是搭建了 model -> view 的管道,這樣 FormControl 對(duì)象值發(fā)生改變時(shí),會(huì)同步更新 view 的 value,并讓 NgModel 指令把這個(gè)新值輸出出去。

通過(guò)以上的解釋,就能理解 name 值發(fā)生變化時(shí),input 的 value 是如何自動(dòng)變化的;input 的 value 發(fā)生變化時(shí),name 值是如何自動(dòng)變化的。(最好能一個(gè)個(gè)點(diǎn)擊鏈接查看源碼,效率更高。) 一句話解釋就是:NgModel 指令初始化時(shí)先安裝了兩個(gè)回調(diào)(一個(gè)是 view 變化時(shí)更新 FormControl 對(duì)象 value 值的回調(diào),另一個(gè)是 FormControl 對(duì)象 value 值變化時(shí)更新 view 值的回調(diào)),數(shù)據(jù)流方向從 view -> model 時(shí),更新 FormControl 對(duì)象并拋出攜帶該值的 ngModelChange 事件,數(shù)據(jù)流方向從 model -> view 時(shí),利用 ControlValueAccessor 去更新 view 值,同時(shí)也拋出攜帶該值的 ngModelChange 事件。拋出的 ngModelChange 事件包含新值,模板中的 $event 會(huì)被 @angular/compiler 特殊處理,為 ngModelChange 事件拋出的值。

當(dāng)然,本文沒(méi)有考慮存在 Validators 的情況,如果 input 模板修改為如下代碼:

那該模板除了綁定 NgModel 指令外,還綁定了 RequiredValidator 指令,這樣不管數(shù)據(jù)流方向是 view -> model 還是 model -> view,在數(shù)據(jù)流動(dòng)之前,還需要運(yùn)行驗(yàn)證器,驗(yàn)證數(shù)據(jù)的有效性。這樣 NgModel 的構(gòu)造函數(shù)里就會(huì)包含 一個(gè) RequiredValidator 對(duì)象,然后 把這個(gè) Validator 傳給 FormControl 對(duì)象,最后注冊(cè) validatorChange 回調(diào),這樣以后 FormControl 值更新時(shí)就會(huì) 運(yùn)行 Validators

總之,NgModel 指令來(lái)管理 model <-> view 的數(shù)據(jù)流,內(nèi)部存在一個(gè) FormControl 對(duì)象,用來(lái)讀取存儲(chǔ)值和驗(yàn)證有效性,從 FormControl 讀取的值會(huì)賦值給外界傳進(jìn)來(lái)的 model,view 是借助 ControlValueAccessor 來(lái)讀寫值。整個(gè) @angular/forms 包的設(shè)計(jì)也是按照這種數(shù)據(jù)流形式,并不復(fù)雜。

也可閱讀 @angular/forms 相關(guān)文章了解如何寫一個(gè)自定義的 ControlValueAccessor:譯 別再對(duì) Angular 表單的 ControlValueAccessor 感到迷惑

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/96510.html

相關(guān)文章

  • @angular/forms 源碼解析 Validators

    摘要:校驗(yàn)器運(yùn)行完成后,會(huì)設(shè)置屬性,從而計(jì)算的屬性,假設(shè)校驗(yàn)錯(cuò)誤,則屬性值為。這樣就理解了校驗(yàn)器的整個(gè)運(yùn)行過(guò)程,也包括為何校驗(yàn)錯(cuò)誤時(shí)會(huì)自動(dòng)添加描述控件狀態(tài)的。 我們知道,@angular/forms 包主要用來(lái)解決表單問(wèn)題的,而表單問(wèn)題非常重要的一個(gè)功能就是表單校驗(yàn)功能。數(shù)據(jù)校驗(yàn)非常重要,不僅僅前端在發(fā)請(qǐng)求給后端前需要校驗(yàn)數(shù)據(jù),后端對(duì)前端發(fā)來(lái)的數(shù)據(jù)也需要校驗(yàn)其有效性和邏輯性,尤其在存入數(shù)據(jù)庫(kù)...

    JowayYoung 評(píng)論0 收藏0
  • ANGULAR2 與 D3.js集成實(shí)現(xiàn)自定義可視化

    摘要:目標(biāo)展現(xiàn)層與邏輯層分離數(shù)據(jù)與可視化組件相分離數(shù)據(jù)與視圖雙向綁定實(shí)時(shí)更新代碼結(jié)構(gòu)清晰易于維護(hù)與修改基本原理的組件生命周期鉤子方法父子組件交互機(jī)制模板語(yǔ)法源碼解析代碼結(jié)構(gòu)很簡(jiǎn)單,其中除主頁(yè)和之外的代碼結(jié)構(gòu)如下所示實(shí)現(xiàn)宿主視圖定義,個(gè)按鈕,按鈕 目標(biāo) 展現(xiàn)層與邏輯層分離 數(shù)據(jù)與可視化組件相分離 數(shù)據(jù)與視圖雙向綁定,實(shí)時(shí)更新 代碼結(jié)構(gòu)清晰,易于維護(hù)與修改 基本原理 angular2 的組件...

    wangbjun 評(píng)論0 收藏0
  • Angular 4 簡(jiǎn)單入門筆記

    摘要:首先,我們需要在入口頁(yè)面的中配置根路徑然后創(chuàng)建一個(gè)路由模塊路由配置在主模塊中導(dǎo)入配置好的路由模塊而在頁(yè)面中需要一個(gè)容器去承載上面代碼中的定義了用戶點(diǎn)擊后的路由跳轉(zhuǎn),定義該路由激活時(shí)的樣式類。 剛實(shí)習(xí)的時(shí)候用過(guò)AngularJS,那時(shí)候真的是連原生JavaScript都不會(huì)寫,依樣畫葫蘆做了幾個(gè)管理后臺(tái)。然后突然換項(xiàng)目了,AngularJS就不寫了,感覺(jué)前前后后接觸了一年多的Angula...

    whlong 評(píng)論0 收藏0
  • angular2初入眼簾-多components協(xié)作

    摘要:我們使用了模式書寫,并引入了思想,這些以前只在里見到的設(shè)計(jì),現(xiàn)在里也有體現(xiàn),并且在本章中會(huì)著重講解多的協(xié)作。如果之前寫過(guò),那對(duì)于這種書寫方式一定無(wú)比熟悉。每次數(shù)據(jù)的變更,無(wú)論是還是,都將變化冒泡到,然后由再向下逐級(jí)推送各組件是否重繪。 前集回顧 在上一章里我們講了如何在angular2下開發(fā)一個(gè)component(還沒(méi)做的趕緊去學(xué)吧)。我們使用了Unidirectional Data ...

    dreamans 評(píng)論0 收藏0
  • angular4 學(xué)習(xí)記錄 -- 表單

    摘要:在表單上添加的會(huì)攔截標(biāo)準(zhǔn)的表單提交事件。并為它們提供了一些共同的行為和屬性,其中有些是可觀察對(duì)象。用于跟蹤一個(gè)單獨(dú)的表單控件的值和有效性狀態(tài)。組件中的頂級(jí)表單就是一個(gè)。在表單所在的中的上添加,再在指定的驗(yàn)證方法中調(diào)用來(lái)顯示驗(yàn)證失敗信息。 angular4 表單 模板表單 在app.module中導(dǎo)入FormsModule之后,項(xiàng)目中的form表單都會(huì)是一個(gè)ngForm,也就是一個(gè)模板表...

    james 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<