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 相关文章推荐
js中根据字数截取字符串,不能截断url
Jan 12 Javascript
js怎么终止程序return不行换jfslk
May 30 Javascript
js控制input框只读实现示例
Jan 20 Javascript
jQuery中prevUntil()方法用法实例
Jan 08 Javascript
高效的jquery数字滚动特效
Dec 17 Javascript
jquery插件之文字间歇自动向上滚动效果代码
Feb 25 Javascript
jQuery中数据缓存$.data的用法及源码完全解析
Apr 29 Javascript
js 上传文件预览的简单实例
Aug 16 Javascript
Easyui的组合框的取值与赋值
Oct 28 Javascript
浅谈Angular单元测试总结
Mar 22 Javascript
iview的table组件自带的过滤器实现
Jul 12 Javascript
vue中的v-model原理,与组件自定义v-model详解
Aug 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
php下将XML转换为数组
2010/01/01 PHP
php下通过IP获取地理位置的代码(小偷程序)
2011/06/09 PHP
修改php.ini以达到屏蔽错误信息并记录日志
2013/06/16 PHP
基于PHP中的常用函数回顾
2013/07/11 PHP
Zend Framework动作控制器用法示例
2016/12/09 PHP
Yii CFileCache 获取不到值的原因分析
2017/02/08 PHP
php实现的mongoDB单例模式操作类
2018/01/20 PHP
修复ie8&amp;chrome下window的resize事件多次执行
2011/10/20 Javascript
jQuery之选择组件的深入解析
2013/06/19 Javascript
初步了解javascript面向对象
2015/11/09 Javascript
基于Jquery代码实现手风琴菜单
2015/11/19 Javascript
jQuery+css3实现转动的正方形效果(附demo源码下载)
2016/01/27 Javascript
浅谈Sticky组件的改进实现
2016/03/22 Javascript
JS定时器用法分析【时钟与菜单中的应用】
2016/12/21 Javascript
jQuery实现验证码功能
2017/03/17 Javascript
json的结构与遍历方法实例分析
2017/04/25 Javascript
Vue.js常用指令的使用小结
2017/06/23 Javascript
Vue多系统切换实现方案
2018/06/05 Javascript
微信小程序提交form操作示例
2018/12/30 Javascript
JSON基本语法及与JavaScript的异同实例分析
2019/01/04 Javascript
javascript 内存模型实例详解
2020/04/18 Javascript
PHP 502bad gateway原因及解决方案
2020/11/13 Javascript
使用C语言扩展Python程序的简单入门指引
2015/04/14 Python
用Python实现一个简单的能够上传下载的HTTP服务器
2015/05/05 Python
Python 迭代器工具包【推荐】
2016/05/06 Python
Scrapy抓取京东商品、豆瓣电影及代码分享
2017/11/23 Python
TensorFlow实现模型评估
2018/09/07 Python
10 分钟快速入门 Python3的教程
2019/01/29 Python
50行Python代码实现视频中物体颜色识别和跟踪(必须以红色为例)
2019/11/20 Python
python3 使用Opencv打开USB摄像头,配置1080P分辨率的操作
2019/12/11 Python
让IE6、IE7、IE8支持CSS3的脚本
2010/07/20 HTML / CSS
使用phonegap播放音频的实现方法
2017/03/31 HTML / CSS
27个经典Linux面试题及答案,你知道几个?
2013/01/10 面试题
含预算的公司户外活动方案
2014/08/16 职场文书
考勤制度通知
2015/04/25 职场文书
Python序列化与反序列化相关知识总结
2021/06/08 Python