angular4自定义组件非input元素实现ngModel双向数据绑定的方法


Posted in Javascript onDecember 28, 2018

在angular里我们一般都是给input元素添加[(ngModel)]="value"实现数据双向绑定,如果想实现自定义的组件上实现ngModel双向数据绑定应该怎么办呐。。。

网上找了一下,没看懂记录一下。

场景:组件能获取父组件通过ngModel绑定的值,能通过ngModel改变父组件对应的数据。如下代码:

<app-child [(ngModel])="appData"></app-child>

1、先贴出效果图:

angular4自定义组件非input元素实现ngModel双向数据绑定的方法

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) => {}; // 先定义一个方法,很重要,用于接收registerOnChange()方法里传递回来的方法,然后通过这个方法就能通知到外部组件数据更新。
 set childData(value: number) { // childData被更改走该方法
  this._data = value;
  this.change(this._data); // 将更新后的数据通知到外部组件
 }
 get childData() { // 页面或者方法里面有调用childData就会走该方法
  return this._data;
 }
 writeValue(val): void { // 初始化时,获取并监听父组件通过ngModel传递进来的数据
  if (val) {
   this._data = val;
  }
 }
 registerOnChange(fn: any): void { // 初始化后,执行该方法,并保存控件接收到 change 事件后,调用的函数
  this.change = fn;
 }
 registerOnTouched(fn: any): void {
 }
}

3、下面开始说下实现的过程吧:

如果添加ngModel后报如下错误,检查组件对应的Module文件有没有导入FormsModule

angular4自定义组件非input元素实现ngModel双向数据绑定的方法

import { FormsModule } from '@angular/forms';
@NgModule({
 ...
 imports: [
  ...,
  FormsModule
 ],
 ...
})

import FormsModule后,控制台任然会报错:

angular4自定义组件非input元素实现ngModel双向数据绑定的方法

这是因为我们需要在使用ngModel的组件里实现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), // 这里的组件名为当前组件的名字
  multi: true
 }]
})
export class ChildComponent implements ControlValueAccessor {
 constructor() { }
 childData = 2;
}

处理完成后控制台的报错信息已经改变:

angular4自定义组件非input元素实现ngModel双向数据绑定的方法

这是因为ControlValueAccessor的接口有几个必须存在的方法,会自动去调用:

writeValue(val): void {
 }
 registerOnChange(fn: any): void {
 }
 registerOnTouched(fn: any): void {
 }
  • 初始化的时候调用  writeValue()  方法,将会使用表单模型中对应的初始值作为参数(也就是ngModel里的值)。
  • registerOnChange() 可以用来通知外部,组件已经发生变化。
  • registerOnTouched() 方法用于设置当控件接收到 touched 事件后,调用的函数。

知道了这三个方法后,我们就可以在writeValue方法里给组件设置父组件通过ngModel传递过来的值了。如:

writeValue(val): void {
 if (val) {
  this.childData = val;
 }
}

那么怎么将组件里更新的数据传递给父组件呐。

registerOnChange(fn: any): void { // 初始化后,执行该方法,并保存控件接收到 change 事件后,调用的函数
 this.change = fn;
}

writeValue()方法后就会执行registerOnChange()方法,我们就是通过该方法传递回来的方法参数来通知到外部组件数据更新的,所以我们要在最开始就定义一个方法来接收。

change = (value: any) => {}; 
// 先定义一个方法,很重要,用于接收registerOnChange()方法里传递回来的方法,然后通过这个方法就能通知到外部组件数据更新。

然后就可以通过change方法通知外部组件了

set childData(value: number) { // childData被更改走该方法
 this._data = value;
 this.change(this._data); // 将更新后的数据通知到外部组件
}

最开始贴出来的代码,中间使用了set 和get去处理了数据,在get childData()方法里打断点发现会执行很多次该方法,其实也可以修改成通过更新数据的时候就直接调用change()方法来通知外部组件数据更新,如下:

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) => {}; // 先定义一个方法,很重要,用于接收registerOnChange()方法里传递回来的方法,然后通过这个方法就能通知到外部组件数据更新。
 writeValue(val): void { // 初始化时,获取并监听父组件通过ngModel传递进来的数据
  if (val) {
   this.childData = val;
  }
 }
 registerOnChange(fn: any): void { // 初始化后,执行该方法,并保存控件接收到 change 事件后,调用的函数
  this.change = fn;
 }
 registerOnTouched(fn: any): void {
 }
}

中间不用使用get和set,不知道两种方法哪种更好。

其实通过子组件通知父级组件数据更新,可以使用@Input和@Output来实现的,如果是@Input获取的父级组件的数据,父级组件数据更新,子组件需要在ngOnChanges生命周期里去监听对应的数据变更并处理相应的逻辑。

不过在自定义组件上使用ngModel实现数据的双向绑定还可以用作表单处理上,比如表单模板和表单验证。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript 操作Word和Excel的实现代码
Oct 26 Javascript
js操作时间(年-月-日 时-分-秒 星期几)
Jun 20 Javascript
JS根据变量保存方法名并执行方法示例
Apr 04 Javascript
javascript实现完美拖拽效果
May 06 Javascript
jquery选择器简述
Aug 31 Javascript
基于JavaScript实现div层跟随滚动条滑动
Jan 12 Javascript
javascript实现平滑无缝滚动
Aug 09 Javascript
JavaScript仿百度图片浏览效果
Nov 23 Javascript
关于JavaScript中forEach和each用法浅析
Jul 27 Javascript
关于jquery中attr()和prop()方法的区别
May 28 jQuery
node.js之基础加密算法模块crypto详解
Sep 11 Javascript
layui实现tab的添加拒绝重复的方法
Sep 04 Javascript
小程序文字跑马灯效果
Dec 28 #Javascript
JS实现的图片选择顺序切换和循环切换功能示例【测试可用】
Dec 28 #Javascript
Angular使用Restful的增删改
Dec 28 #Javascript
原生js实现公告滚动效果
Jan 10 #Javascript
微信小程序实现文字无限轮播效果
Dec 28 #Javascript
小程序实现左右来回滚动字幕效果
Dec 28 #Javascript
原生JS实现的自动轮播图功能详解
Dec 28 #Javascript
You might like
2020年4月放送决定!第2期TV动画《邪神酱飞踢》视觉图&主题曲情报公开!
2020/03/06 日漫
CI框架源码阅读,系统常量文件constants.php的配置
2013/02/28 PHP
JS 自定义函数缺省值的设置方法
2010/05/05 Javascript
javascript 精粹笔记
2010/05/09 Javascript
自制基于jQuery的智能提示插件一枚
2011/02/18 Javascript
jquery validate poshytip 自定义样式
2012/11/26 Javascript
基于dom编程中 动态创建与删除元素的使用
2013/04/17 Javascript
使用javascript实现页面定时跳转总结篇
2013/09/21 Javascript
扩展IE中一些不兼容的方法如contains、startWith等等
2014/01/09 Javascript
jQuery的animate函数学习记录
2014/08/08 Javascript
又一款js时钟!transform实现时钟效果
2016/08/15 Javascript
js实现Tab选项卡切换效果
2020/07/17 Javascript
VUE实现强制渲染,强制更新
2019/10/29 Javascript
three.js利用gpu选取物体并计算交点位置的方法示例
2019/11/25 Javascript
vue实现循环滚动列表
2020/06/30 Javascript
浅谈vue单页面中有多个echarts图表时的公用代码写法
2020/07/19 Javascript
python回调函数用法实例分析
2015/05/09 Python
python迭代器与生成器详解
2016/03/10 Python
Python简单的制作图片验证码实例
2017/05/31 Python
Django 如何获取前端发送的头文件详解(推荐)
2017/08/15 Python
python定时复制远程文件夹中所有文件
2019/04/30 Python
python集合的创建、添加及删除操作示例
2019/10/08 Python
分享一个python的aes加密代码
2020/12/22 Python
Python如何实现感知器的逻辑电路
2020/12/25 Python
python使用scapy模块实现ARP扫描的过程
2021/01/21 Python
html5 canvas 使用示例
2010/10/22 HTML / CSS
实例教程 利用html5和css3打造一款创意404页面
2014/10/20 HTML / CSS
倩碧英国官网:Clinique英国
2018/08/10 全球购物
尼克松手表官网:Nixon手表
2019/03/17 全球购物
机电一体化专业应届本科生求职信
2013/09/27 职场文书
大学生工作自荐书
2014/06/16 职场文书
化工专业自荐书
2014/06/16 职场文书
音乐学专业求职信
2014/07/22 职场文书
私营公司诉讼代理委托书范本
2014/09/13 职场文书
2015年幼儿园卫生保健工作总结
2015/05/12 职场文书
情况说明书格式及范文
2019/06/24 职场文书