angular4自定義組件非input元素實(shí)現(xiàn)ngModel雙向數(shù)據(jù)綁定的方法
在angular里我們一般都是給input元素添加[(ngModel)]="value"實(shí)現(xiàn)數(shù)據(jù)雙向綁定,如果想實(shí)現(xiàn)自定義的組件上實(shí)現(xiàn)ngModel雙向數(shù)據(jù)綁定應(yīng)該怎么辦吶。。。
網(wǎng)上找了一下,沒看懂記錄一下。
場(chǎng)景:組件能獲取父組件通過ngModel綁定的值,能通過ngModel改變父組件對(duì)應(yīng)的數(shù)據(jù)。如下代碼:
<app-child [(ngModel])="appData"></app-child>
1、先貼出效果圖:
2、下面是app-child組件的代碼:
import { Component, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ChildComponent), multi: true }] }) export class ChildComponent implements ControlValueAccessor { constructor() { } _data: any; add () { this.childData ++; } change = (value: any) => {}; // 先定義一個(gè)方法,很重要,用于接收registerOnChange()方法里傳遞回來的方法,然后通過這個(gè)方法就能通知到外部組件數(shù)據(jù)更新。 set childData(value: number) { // childData被更改走該方法 this._data = value; this.change(this._data); // 將更新后的數(shù)據(jù)通知到外部組件 } get childData() { // 頁(yè)面或者方法里面有調(diào)用childData就會(huì)走該方法 return this._data; } writeValue(val): void { // 初始化時(shí),獲取并監(jiān)聽父組件通過ngModel傳遞進(jìn)來的數(shù)據(jù) if (val) { this._data = val; } } registerOnChange(fn: any): void { // 初始化后,執(zhí)行該方法,并保存控件接收到 change 事件后,調(diào)用的函數(shù) this.change = fn; } registerOnTouched(fn: any): void { } }
3、下面開始說下實(shí)現(xiàn)的過程吧:
如果添加ngModel后報(bào)如下錯(cuò)誤,檢查組件對(duì)應(yīng)的Module文件有沒有導(dǎo)入FormsModule
import { FormsModule } from '@angular/forms'; @NgModule({ ... imports: [ ..., FormsModule ], ... })
import FormsModule后,控制臺(tái)任然會(huì)報(bào)錯(cuò):
這是因?yàn)槲覀冃枰谑褂胣gModel的組件里實(shí)現(xiàn)ControlValueAccessor的接口方法。
先引入和使用我們必須使用的配置:
import { Component, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ChildComponent), // 這里的組件名為當(dāng)前組件的名字 multi: true }] }) export class ChildComponent implements ControlValueAccessor { constructor() { } childData = 2; }
處理完成后控制臺(tái)的報(bào)錯(cuò)信息已經(jīng)改變:
這是因?yàn)镃ontrolValueAccessor的接口有幾個(gè)必須存在的方法,會(huì)自動(dòng)去調(diào)用:
writeValue(val): void { } registerOnChange(fn: any): void { } registerOnTouched(fn: any): void { }
- 初始化的時(shí)候調(diào)用
writeValue()
方法,將會(huì)使用表單模型中對(duì)應(yīng)的初始值作為參數(shù)(也就是ngModel里的值)。 - registerOnChange() 可以用來通知外部,組件已經(jīng)發(fā)生變化。
- registerOnTouched() 方法用于設(shè)置當(dāng)控件接收到 touched 事件后,調(diào)用的函數(shù)。
知道了這三個(gè)方法后,我們就可以在writeValue方法里給組件設(shè)置父組件通過ngModel傳遞過來的值了。如:
writeValue(val): void { if (val) { this.childData = val; } }
那么怎么將組件里更新的數(shù)據(jù)傳遞給父組件吶。
registerOnChange(fn: any): void { // 初始化后,執(zhí)行該方法,并保存控件接收到 change 事件后,調(diào)用的函數(shù) this.change = fn; }
writeValue()方法后就會(huì)執(zhí)行registerOnChange()方法,我們就是通過該方法傳遞回來的方法參數(shù)來通知到外部組件數(shù)據(jù)更新的,所以我們要在最開始就定義一個(gè)方法來接收。
change = (value: any) => {}; // 先定義一個(gè)方法,很重要,用于接收registerOnChange()方法里傳遞回來的方法,然后通過這個(gè)方法就能通知到外部組件數(shù)據(jù)更新。
然后就可以通過change方法通知外部組件了
set childData(value: number) { // childData被更改走該方法 this._data = value; this.change(this._data); // 將更新后的數(shù)據(jù)通知到外部組件 }
最開始貼出來的代碼,中間使用了set 和get去處理了數(shù)據(jù),在get childData()方法里打斷點(diǎn)發(fā)現(xiàn)會(huì)執(zhí)行很多次該方法,其實(shí)也可以修改成通過更新數(shù)據(jù)的時(shí)候就直接調(diào)用change()方法來通知外部組件數(shù)據(jù)更新,如下:
import { Component, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ChildComponent), multi: true }] }) export class ChildComponent implements ControlValueAccessor { constructor() { } _data: any; childData = 1; add () { this.childData ++; this.change(this.childData); } change = (value: any) => {}; // 先定義一個(gè)方法,很重要,用于接收registerOnChange()方法里傳遞回來的方法,然后通過這個(gè)方法就能通知到外部組件數(shù)據(jù)更新。 writeValue(val): void { // 初始化時(shí),獲取并監(jiān)聽父組件通過ngModel傳遞進(jìn)來的數(shù)據(jù) if (val) { this.childData = val; } } registerOnChange(fn: any): void { // 初始化后,執(zhí)行該方法,并保存控件接收到 change 事件后,調(diào)用的函數(shù) this.change = fn; } registerOnTouched(fn: any): void { } }
中間不用使用get和set,不知道兩種方法哪種更好。
其實(shí)通過子組件通知父級(jí)組件數(shù)據(jù)更新,可以使用@Input和@Output來實(shí)現(xiàn)的,如果是@Input獲取的父級(jí)組件的數(shù)據(jù),父級(jí)組件數(shù)據(jù)更新,子組件需要在ngOnChanges生命周期里去監(jiān)聽對(duì)應(yīng)的數(shù)據(jù)變更并處理相應(yīng)的邏輯。
不過在自定義組件上使用ngModel實(shí)現(xiàn)數(shù)據(jù)的雙向綁定還可以用作表單處理上,比如表單模板和表單驗(yàn)證。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
深入淺析AngularJS中的一次性數(shù)據(jù)綁定 (bindonce)
這篇文章主要介紹了AngularJS中的一次性數(shù)據(jù)綁定 (bindonce)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-05-05創(chuàng)建你的第一個(gè)AngularJS應(yīng)用的方法
這篇文章主要介紹了創(chuàng)建你的第一個(gè)AngularJS應(yīng)用的方法,AngularJS是一個(gè)非常具有人氣的JavaScript框架,需要的朋友可以參考下2015-06-06Javascript基礎(chǔ)_標(biāo)記文字的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狫avascript基礎(chǔ)_標(biāo)記文字的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06解決angularjs service中依賴注入$scope報(bào)錯(cuò)的問題
今天小編就為大家分享一篇解決angularjs service中依賴注入$scope報(bào)錯(cuò)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10angularjs 指令實(shí)現(xiàn)自定義滾動(dòng)條效果
橫向商品欄,把原有的滾動(dòng)條改成自定義的樣式,并且給兩邊加上箭頭可以調(diào)整,可以拖動(dòng)商品和滾輪實(shí)現(xiàn)滾動(dòng)條效果,這篇文章主要介紹了angularjs 指令實(shí)現(xiàn)自定義滾動(dòng)條效果,需要的朋友可以參考下2024-03-03