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 的继承
Oct 01 Javascript
jQuery学习笔记 操作jQuery对象 属性处理
Sep 19 Javascript
js中function()使用方法
Dec 24 Javascript
JavaScript中的函数声明和函数表达式区别浅析
Mar 27 Javascript
JavaScript动态改变div属性的实现方法
Jul 22 Javascript
jquery实现多条件筛选特效代码分享
Aug 28 Javascript
js实现带简单弹性运动的导航条
Feb 22 Javascript
捕获未处理的Promise错误方法
Oct 13 Javascript
vue编译打包本地查看index文件的方法
Feb 23 Javascript
vue+elementUI实现简单日历功能
Sep 24 Javascript
JavaScript实现滑块验证解锁
Jan 07 Javascript
vue本地构建热更新卡顿的问题“75 advanced module optimization”完美解决方案
Aug 05 Vue.js
小程序文字跑马灯效果
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 IDE phpstorm 常用快捷键
2015/05/18 PHP
php函数mkdir实现递归创建层级目录
2016/10/27 PHP
php如何比较两个浮点数是否相等详解
2019/02/12 PHP
jquery1.4.2 for Visual studio 2010 模板文件
2010/07/14 Javascript
THREE.JS入门教程(4)创建粒子系统
2013/01/24 Javascript
javascript if条件判断方法小结
2014/05/17 Javascript
jquery隔行换色效果实现方法
2015/01/15 Javascript
jQuery中extend函数详解
2015/02/13 Javascript
jQuery实现不断闪烁文字的方法
2015/05/15 Javascript
AngularJS自动表单验证
2016/02/01 Javascript
微信小程序 选择器(时间,日期,地区)实例详解
2016/11/16 Javascript
关于javascript作用域的常见面试题分享
2017/06/18 Javascript
LayUI switch 开关监听 获取属性值、更改状态的方法
2019/09/21 Javascript
vue 实现在同一界面实现组件的动态添加和删除功能
2020/06/16 Javascript
Vue中nprogress页面加载进度条的方法实现
2020/11/13 Javascript
Python Django 添加首页尾页上一页下一页代码实例
2019/08/21 Python
python_array[0][0]与array[0,0]的区别详解
2020/02/18 Python
用python介绍4种常用的单链表翻转的方法小结
2020/02/24 Python
python数据分析工具之 matplotlib详解
2020/04/09 Python
python正则表达式 匹配反斜杠的操作方法
2020/08/07 Python
美国第二大连锁书店:Books-A-Million
2017/12/28 全球购物
班组长安全职责
2014/01/05 职场文书
保卫科工作岗位职责
2014/03/01 职场文书
关爱女孩行动实施方案
2014/03/13 职场文书
夫妻双方自愿离婚协议书怎么写
2014/12/01 职场文书
商铺租房协议书范本
2014/12/04 职场文书
小时代观后感
2015/06/10 职场文书
债务追讨律师函
2015/06/24 职场文书
养成教育主题班会
2015/08/13 职场文书
2019交通安全宣传标语集锦!
2019/06/28 职场文书
导游词之贵州织金洞
2019/10/12 职场文书
Python import模块的缓存问题解决方案
2021/06/02 Python
mysql备份策略的实现(全量备份+增量备份)
2021/07/07 MySQL
一文带你探究MySQL中的NULL
2021/11/11 MySQL
python 实现图片特效处理
2022/04/03 Python
NoSQL优缺点与MongoDB数据库简介
2022/06/05 MongoDB