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 相关文章推荐
var与Javascript变量隐式声明
Sep 17 Javascript
jQuery中对节点进行操作的相关介绍
Apr 16 Javascript
全面兼容的javascript时间格式化函数(比较实用)
May 14 Javascript
简介JavaScript中POSITIVE_INFINITY值的使用
Jun 05 Javascript
ES6新数据结构Set与WeakSet用法分析
Mar 31 Javascript
JavaScript使用readAsDataUrl方法预览图片
May 10 Javascript
Node.js 使用命令行工具检查更新
Jun 08 Javascript
原生js实现Flappy Bird小游戏
Dec 24 Javascript
如何在Vue中抽离接口配置文件
Oct 31 Javascript
微信小程序如何实现radio单选框单击打勾和取消
Jan 21 Javascript
实例讲解React 组件生命周期
Jul 08 Javascript
vue使用openlayers实现移动点动画
Sep 24 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将数据库导出成excel的方法
2010/05/07 PHP
在项目中寻找代码的坏命名
2012/07/14 PHP
php+js实现异步图片上传实例分享
2014/06/02 PHP
PHP队列用法实例
2014/11/05 PHP
Laravel6.2中用于用户登录的新密码确认流程详解
2019/10/16 PHP
jQuery ajax serialize()方法的使用以及常见问题解决
2013/01/27 Javascript
Javascript跨域请求的4种解决方式
2013/03/17 Javascript
Javascript中的Array数组对象详谈
2014/03/03 Javascript
Node.js 中exports 和 module.exports 的区别
2017/03/14 Javascript
Vue中保存用户登录状态实例代码
2017/06/07 Javascript
如何从零开始利用js手写一个Promise库详解
2018/04/19 Javascript
JS/CSS实现字符串单词首字母大写功能
2019/09/03 Javascript
[40:53]完美世界DOTA2联赛PWL S3 Magma vs DLG 第二场 12.18
2020/12/20 DOTA
python 排列组合之itertools
2013/03/20 Python
简介Django框架中可使用的各类缓存
2015/07/23 Python
python直接访问私有属性的简单方法
2016/07/25 Python
python读取文件名称生成list的方法
2018/04/27 Python
Python封装成可带参数的EXE安装包实例
2019/08/24 Python
使用OpenCV实现仿射变换—旋转功能
2019/08/29 Python
使用jupyter Nodebook查看函数或方法的参数以及使用情况
2020/04/14 Python
PythonPC客户端自动化实现原理(pywinauto)
2020/05/28 Python
Python Django路径配置实现过程解析
2020/11/05 Python
关于webview适配H5上传照片或者视频文件的方法
2020/11/04 HTML / CSS
纽约通行卡:The New York Pass(免费游览纽约90多个景点)
2017/07/29 全球购物
新加坡网上美容店:Hermo新加坡
2019/06/19 全球购物
拾金不昧表扬信范文
2014/01/11 职场文书
五一口号
2014/06/19 职场文书
餐厅保洁员岗位职责
2015/04/10 职场文书
2015年会计年终工作总结
2015/05/26 职场文书
幼儿园托班开学寄语(2016秋季)
2015/12/03 职场文书
golang 实现菜单树的生成方式
2021/04/28 Golang
Jupyter Notebook内使用argparse报错的解决方案
2021/06/03 Python
mongodb的安装和开机自启动详细讲解
2021/08/02 MongoDB
详解Nginx 被动检查服务器的存活状态
2021/10/16 Servers
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
2022/04/20 MySQL
使用 MybatisPlus 连接 SqlServer 数据库解决 OFFSET 分页问题
2022/04/22 SQL Server