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 相关文章推荐
类之Prototype.js学习
Jun 13 Javascript
超棒的javascript页面顶部卷动广告效果
Dec 01 Javascript
ExtJs3.0中Store添加 baseParams 的Bug
Mar 10 Javascript
jquery checkbox,radio是否选中的判断代码
Mar 20 Javascript
js实现数组去重、判断数组以及对象中的内容是否相同
Nov 29 Javascript
jQuery实现可关闭固定于底(顶)部的工具条菜单效果
Nov 06 Javascript
全面解析DOM操作和jQuery实现选项移动操作代码分享
Jun 07 Javascript
bootstrap表单按回车会自动刷新页面的解决办法
Mar 08 Javascript
AngularJS折叠菜单实现方法示例
May 18 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
Jul 13 Javascript
js监听html页面的上下滚动事件方法
Sep 11 Javascript
帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
Aug 23 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 页面执行时间计算代码
2008/12/04 PHP
php 无限分类的树类代码
2009/12/03 PHP
ThinkPHP打开验证码页面显示乱码的解决方法
2014/12/18 PHP
php生成不重复随机数、数组的4种方法分享
2015/03/30 PHP
php使用curl通过代理获取数据的实现方法
2016/05/16 PHP
PHP 常用时间函数资料整理
2016/10/22 PHP
详解php中 === 的使用
2016/10/24 PHP
对laravel的session获取与存取方法详解
2019/10/08 PHP
IE php关于强制下载文件的代码
2008/08/23 Javascript
改善你的jQuery的25个步骤 千倍级效率提升
2010/02/11 Javascript
jQuery 借助插件Lavalamp实现导航条动态美化效果
2013/09/27 Javascript
nodejs分页类代码分享
2014/06/17 NodeJs
javascript学习笔记之函数定义
2015/06/25 Javascript
小议JavaScript中Generator和Iterator的使用
2015/07/29 Javascript
js带缩略图的图片轮播效果代码分享
2015/09/14 Javascript
jQuery超简单选项卡完整实例
2015/09/26 Javascript
JavaScript对HTML DOM使用EventListener进行操作
2015/10/21 Javascript
有关easyui-layout中的收缩层无法显示标题的解决办法
2016/05/10 Javascript
JS中检测数据类型的几种方式及优缺点小结
2016/12/12 Javascript
vue elementui form表单验证的实现
2018/11/11 Javascript
python web框架学习笔记
2016/05/03 Python
Python时间的精准正则匹配方法分析
2017/08/17 Python
python求最大连续子数组的和
2018/07/07 Python
浅谈pyqt5中信号与槽的认识
2019/02/17 Python
python通过paramiko复制远程文件及文件目录到本地
2019/04/30 Python
python 截取XML中bndbox的坐标中的图像,另存为jpg的实例
2020/03/10 Python
python matplotlib 绘图 和 dpi对应关系详解
2020/03/14 Python
Python爬虫如何破解JS加密的Cookie
2020/11/19 Python
python反编译教程之2048小游戏实例
2021/03/03 Python
在阿尔卑斯山或希腊度过快乐假期:Alpine Elements
2019/12/28 全球购物
opencv实现图像几何变换
2021/03/24 Python
员工试用期考核自我鉴定
2014/04/13 职场文书
态度决定一切演讲稿
2014/05/20 职场文书
竞聘报告优秀范文
2014/11/06 职场文书
团员个人总结
2015/02/26 职场文书
Vue监视数据的原理详解
2022/02/24 Vue.js