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 相关文章推荐
ie7+背景透明文字不透明超级简单的实现方法
Jan 17 Javascript
javascript获取函数名称、函数参数、对象属性名称的代码实例
Apr 12 Javascript
JavaScript实现可拖拽的拖动层Div实例
Aug 05 Javascript
javascript设计模式之中介者模式学习笔记
Feb 15 Javascript
jQuery插件HighCharts实现的2D条状图效果示例【附demo源码下载】
Mar 15 Javascript
vue2.0使用swiper组件实现轮播效果
Nov 27 Javascript
vue实现通讯录功能
Jul 14 Javascript
在Vant的基础上实现添加表单验证框架的方法示例
Dec 05 Javascript
Vue.js组件通信之自定义事件详解
Oct 19 Javascript
使用Node.js实现base64和png文件相互转换的方法
Mar 11 Javascript
JavaScript经典案例之简易计算器
Aug 24 Javascript
JavaScript 实现轮播图特效的示例
Nov 05 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
BBS(php &amp; mysql)完整版(一)
2006/10/09 PHP
php中使用Curl、socket、file_get_contents三种方法POST提交数据
2011/08/12 PHP
php和html的区别点详细总结
2019/09/24 PHP
javascript RadioButtonList获取选中值
2009/04/09 Javascript
JavaScript 无符号右移运算符
2009/04/17 Javascript
cookie.js 加载顺序问题怎么才有效
2013/07/31 Javascript
jquery交替变换颜色的三种方法 实例代码
2013/11/19 Javascript
js取float型小数点后两位数的方法
2014/01/18 Javascript
Jquery在指定DIV加载HTML示例代码
2014/02/17 Javascript
改变隐藏的input中value值的方法
2014/03/19 Javascript
js遍历子节点子元素附属性及方法
2014/08/19 Javascript
javascript获取flash版本号的方法
2014/11/20 Javascript
NodeJS学习笔记之Http模块
2015/01/13 NodeJs
bootstrap的3级菜单样式,支持母版页保留打开状态实现方法
2016/11/10 Javascript
简单理解js的冒泡排序
2016/12/19 Javascript
Vue实现virtual-dom的原理简析
2017/07/10 Javascript
React Native 环境搭建的教程
2017/08/19 Javascript
ES6学习教程之块级作用域详解
2017/10/09 Javascript
Vue2 模板template的四种写法总结
2018/02/23 Javascript
解决Vue打包上线之后部分CSS不生效的问题
2019/11/12 Javascript
js+html+css实现手动轮播和自动轮播
2020/12/30 Javascript
Webpack3+React16代码分割的实现
2021/03/03 Javascript
Python中endswith()函数的基本使用
2015/04/07 Python
简单说明Python中的装饰器的用法
2015/04/24 Python
Python实现矩阵相乘的三种方法小结
2018/07/26 Python
python错误调试及单元文档测试过程解析
2019/12/19 Python
Python命名空间及作用域原理实例解析
2020/08/12 Python
5 分钟读懂Python 中的 Hook 钩子函数
2020/12/09 Python
PAUL HEWITT手表美国站:德国北部时尚生活配饰品牌,船锚元素
2017/11/18 全球购物
Speedo澳大利亚官网:全球领先游泳品牌
2018/02/04 全球购物
考博自荐信
2013/10/25 职场文书
2014年端午节演讲稿范文
2014/05/23 职场文书
本科毕业生自荐信
2014/05/26 职场文书
诉讼授权委托书
2014/10/15 职场文书
2016年社会管理综治宣传月活动总结
2016/03/16 职场文书
jdbc使用PreparedStatement批量插入数据的方法
2021/04/27 MySQL