在angular里我们一般都是给input元素添加[(ngModel)]="value"实现数据双向绑定,如果想实现自定义的组件上实现ngModel双向数据绑定应该怎么办呐。。。

网上找了一下,没看懂记录一下。

场景:组件能获取父组件通过ngModel绑定的值,能通过ngModel改变父组件对应的数据。如下代码: 

<app-child [(ngModel])="appData"></app-child>

1、先贴出效果图:

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

import { FormsModule } from '@angular/forms';
@NgModule({
...
imports: [
...,
FormsModule
],
...
})

  import FormsModule后,控制台任然会报错:

  这是因为我们需要在使用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;
}

  处理完成后控制台的报错信息已经改变:

  这是因为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实现数据的双向绑定还可以用作表单处理上,比如表单模板和表单验证。

angular4自定义组件非input元素实现ngModel双向数据绑定的更多相关文章

  1. angular学习笔记(四)- input元素的ng-model属性

    input元素的ng-model属性: 用于将input的值和变量的值进行双向绑定 <!DOCTYPE html> <html ng-app> <head> < ...

  2. 前端 | 自定义组件 v-model:Vue 如何实现双向绑定

    v-model 是 Vue 中一个常用的指令,常用于表单中的数据绑定.如下基本用法想必大家都很熟悉,data 中的 checked 属性的值就会随着多选框的状态实时变化. <el-checkbo ...

  3. 【vue】---- v-model在自定义组件中的使用

    1. v-model简介 可以用 v-model 指令在表单 <input>.<textarea> 及 <select> 元素上创建双向数据绑定,它的本质是一个语法 ...

  4. 组件使用v-model、$listeners、.sync(区别于v-model的双向数据绑定)

    自定义组件 自定义组件的v-model 首先我们先说一下在自定义组件中使用v-model的必要条件 在自定义的组件中要有input(这里我们先不讨论单选复选框) 在自定义组件的模板对象中要有props ...

  5. ionic3+angular4开发混合app 之自定义组件

    这里主要是记录ionic3+angular4开发混合app时自定义组件,我想自定义组件的方法和angular4应该类似,具体在纯angular4中自定义组件,暂时没有实践,个人觉得差别不大,之后实践了 ...

  6. ionic3.x angular4.x ng4.x 自定义组件component双向绑定之自定义计数器

    本文主要示例在ionic3.x环境下实现一个自定义计数器,实现后最终效果如图: 1.使用命令创建一个component ionic g component CounterInput 类似的命令还有: ...

  7. 玩转angularJs——通过自定义ng-model,不仅仅只是input可以实现双向数据绑定

    体验更优排版请移步原文:http://blog.kwin.wang/programming/angularJs-user-defined-ngmodel.html angularJs双向绑定特性在开发 ...

  8. vue input框type=number 保留两位小数自定义组件

    第一步:自定义组件MyNumberInput.vue<template> <input class="numberInput" type="number ...

  9. angular4 自定义表单组件

    自定义表单组件分为单值组件和多值组件. 单值组件:input/select/radio/textarea 多值组件:checkbox/tree组件 条件: 1.必须实现ControlValueAcce ...

随机推荐

  1. 洛谷 P3102 [USACO14FEB]秘密代码Secret Code

    P3102 [USACO14FEB]秘密代码Secret Code 题目描述 Farmer John has secret message that he wants to hide from his ...

  2. git checkout -b 报错

    有时候在git中checkout -b 出现如下报错 $ git checkout -b test --track origin/master fatal: Cannot update paths a ...

  3. Homebrew命令具体解释

    Homebrew命令具体解释 作者:chszs,未经博主同意不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 一.安装Homebrew Shell环境下 ...

  4. 浅析为什么 char 类型的范围是 : -128~+127

    在 C 语言中. signed char 类型的范围为 -128~127,每本教科书上也这么写.可是没有哪一本书上(包含老师)也不会给你为什么是 -128~127,这个问题貌似看起来也非常easyea ...

  5. GraphicsMagick java.io.FileNotFoundException: gm 错误解决办法

    GraphicsMagick java.io.FileNotFoundException: gm 解决办法, 方法一: ProcessStarter.setGlobalSearchPath(" ...

  6. JAVA学习第四十六课 — 其它对象API(二)Date类 &amp; Calendar类(重点掌握)

    Date类(重点) 开发时,会时常遇见时间显示的情况,所以必须熟练Date的应用 <span style="font-family:KaiTi_GB2312;font-size:18p ...

  7. invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

    Column 'dbo.tbm_vie_View.ViewID' is invalid in the select list because it is not contained in either ...

  8. 4.bind绑定

    #include <iostream> #include <string> #include <boost/bind.hpp> #include <boost ...

  9. IHttpHandler的学习(0)

    本片文章转自网络 问题1:什么是HttpHandler?(Handler:处理者:那就是对Http请求的处理拉) 问题2:什么是HttpModule? 问题3:什么时候应该使用HttpHandler什 ...

  10. 【C#Windows 服务】 《二》INI配置文件

    一.工具: VS2015+NET Framework4.5. 二.操作: 1.创建INIHelp帮助类 2.丰富帮助类操作 3.windows实例调用 三.代码: 1.INI帮助类: 1 2 3 4 ...