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 相关文章推荐
window.onload 加载完毕的问题及解决方案(上)
Jul 09 Javascript
基于JavaScript实现继承机制之构造函数+原型链混合方式的使用详解
May 07 Javascript
js脚本实现数据去重
Nov 27 Javascript
JavaScript框架是什么?怎样才能叫做框架?
Jul 01 Javascript
jQuery操作动态生成的内容的方法
May 28 Javascript
SVG实现时钟效果
Jul 17 Javascript
react 父子组件之间通讯props
Sep 08 Javascript
学习jQuery中的noConflict()用法
Sep 28 jQuery
vue.js实现的幻灯片功能示例
Jan 18 Javascript
微信小程序云开发如何使用云函数生成二维码
May 18 Javascript
使用xampp将angular项目运行在web服务器的教程
Sep 16 Javascript
使用Cargo工具高效创建Rust项目
Aug 14 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
理解php Hash函数,增强密码安全
2011/02/25 PHP
自己在做项目过程中学到的PHP知识收集
2012/08/20 PHP
PHP下打开phpMyAdmin出现403错误的问题解决方法
2013/05/23 PHP
ThinkPHP 3.2 数据分页代码分享
2014/10/14 PHP
php简单实现多维数组排序的方法
2016/09/30 PHP
PHP parse_ini_file函数的应用与扩展操作示例
2019/01/07 PHP
利用jq让你的div居中的好方法分享
2013/11/21 Javascript
JS实现网页Div层Clone拖拽效果
2015/09/26 Javascript
Javascript中判断一个值是否为undefined的方法详解
2016/09/28 Javascript
详解angular中如何监控dom渲染完毕
2017/01/03 Javascript
微信小程序联网请求的轮播图
2017/07/07 Javascript
浅谈express 中间件机制及实现原理
2017/08/31 Javascript
jquery中ajax请求后台数据成功后既不执行success也不执行error的完美解决方法
2017/12/24 jQuery
浅谈Koa2框架利用CORS完成跨域ajax请求
2018/03/06 Javascript
vue.js中toast用法及使用toast弹框的实例代码
2018/08/27 Javascript
你应该了解的JavaScript Array.map()五种用途小结
2018/11/14 Javascript
PHP实现基于Redis的MessageQueue队列封装操作示例
2019/02/02 Javascript
vue App.vue中的公共组件改变值触发其他组件或.vue页面监听
2019/05/31 Javascript
JavaScript判断对象和数组的两种方法
2019/05/31 Javascript
Vue项目实现换肤功能的一种方案分析
2019/08/28 Javascript
jQuery操作动画完整实例分析
2020/01/10 jQuery
JavaScript装饰者模式原理与用法实例详解
2020/03/09 Javascript
Python利用pyHook实现监听用户鼠标与键盘事件
2014/08/21 Python
Python XML RPC服务器端和客户端实例
2014/11/22 Python
Python闭包实现计数器的方法
2015/05/05 Python
python各种语言间时间的转化实现代码
2016/03/23 Python
Python查找两个有序列表中位数的方法【基于归并算法】
2018/04/20 Python
Django中的静态文件管理过程解析
2019/08/01 Python
python 用Matplotlib作图中有多个Y轴
2020/11/28 Python
加拿大时尚少女服装品牌:Garage
2016/10/10 全球购物
英国现代绅士品牌:Hackett
2017/12/17 全球购物
PHP面试题附答案
2015/11/28 面试题
写好自荐信的技巧
2013/11/08 职场文书
2014年文艺部工作总结
2014/11/17 职场文书
nginx容器方式反向代理实战
2022/04/18 Servers
Mysql将字符串按照指定字符分割的正确方法
2022/05/30 MySQL