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中删除指定数组中指定的元素的代码
Feb 12 Javascript
Extjs Gird 支持中文拼音排序实现代码
Apr 15 Javascript
判断JS对象是否拥有某种属性的两种方式
Dec 02 Javascript
jquery分页插件jquery.pagination.js实现无刷新分页
Apr 01 Javascript
js实现短信发送倒计时功能(正则验证)
Feb 10 Javascript
jQuery查找dom的几种方法效率详解
May 17 jQuery
jquery请求servlet实现ajax异步请求的示例
Jun 03 jQuery
详解Node.js access_token的获取、存储及更新
Jun 20 Javascript
Bootstrap 模态框多次显示后台提交多次BUG的解决方法
Dec 26 Javascript
node内置调试方法总结
Feb 22 Javascript
IE9 elementUI文件上传的问题解决
Oct 17 Javascript
Vue之beforeEach非登录不能访问的实现(代码亲测)
Jul 18 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
浅析memcache启动以及telnet命令详解
2013/06/28 PHP
PHP、Nginx、Apache中禁止网页被iframe引用的方法
2020/10/01 PHP
为PHP5.4开启Zend OPCode缓存
2014/12/26 PHP
PHP通过文件保存和更新信息的方法分析
2019/09/12 PHP
键盘控制事件应用教程大全
2006/11/24 Javascript
jQuery中after()方法用法实例
2014/12/25 Javascript
javascript中的正则表达式使用指南
2015/03/01 Javascript
javascript中call apply 的应用场景
2015/04/16 Javascript
JS组件Bootstrap ContextMenu右键菜单使用方法
2016/04/17 Javascript
Nodejs进阶:如何将图片转成datauri嵌入到网页中去实例
2016/11/21 NodeJs
Angular的事件和表单详解
2016/12/26 Javascript
JS实现最简单的冒泡排序算法
2017/02/15 Javascript
解决AngualrJS页面刷新导致异常显示问题
2017/04/20 Javascript
vue.js 上传图片实例代码
2017/06/22 Javascript
基于openlayers4实现点的扩散效果
2020/08/17 Javascript
angularjs实现的购物金额计算工具示例
2018/05/08 Javascript
Javascript三种字符串连接方式及性能比较
2019/05/28 Javascript
python操作MySQL数据库具体方法
2013/10/28 Python
python操作摄像头截图实现远程监控的例子
2014/03/25 Python
Go语言基于Socket编写服务器端与客户端通信的实例
2016/02/19 Python
Python爬虫将爬取的图片写入world文档的方法
2018/11/07 Python
Python 中使用 PyMySQL模块操作数据库的方法
2019/11/10 Python
关于pandas的离散化,面元划分详解
2019/11/22 Python
Python tensorflow实现mnist手写数字识别示例【非卷积与卷积实现】
2019/12/19 Python
Python文件操作函数用法实例详解
2019/12/24 Python
浅谈python3 构造函数和析构函数
2020/03/12 Python
时装界的“朋克之母”:Vivienne Westwood
2017/07/06 全球购物
销售会计工作职责
2013/12/02 职场文书
旷课检讨书3000字
2014/02/04 职场文书
客服部班长工作责任制
2014/02/25 职场文书
食品安全处置方案
2014/06/14 职场文书
法学专业求职信
2014/07/15 职场文书
雷锋的故事观后感
2015/06/10 职场文书
2016年元旦寄语
2015/08/17 职场文书
电力企业职工培训心得体会
2016/01/11 职场文书
解除合同协议书范本
2016/03/21 职场文书