本文发布于:2018-11-22,最后更新于:2019-05-23,如果内容失效请留言告知。
在 Angular 中,对于表单元素,通过 [(ngModel)]
即可以简单地实现双向绑定。对于自定义组件而言,希望实现同样的效果可以怎么做呢?
1 实现自定义组件的 ngModel
指令
如果希望自定义组件能够具有与表单元素相同的 ngModel
效果,可以通过在组件内实现 ControlValueAccessor
接口达到目的。
对于 [(ngModel)]
,需要至少实现该接口的如下方法:
1 2 3 4 5 | interface ControlValueAccessor { writeValue(obj: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void } |
最简单的核心实现示例参考如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | import { Component, forwardRef, Input, OnInit, forwardRef } from '@angular/core' ; import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms' ; @Component({ selector: 'custom-input' , template: `<input [(ngModel)]= "value" />`, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CustomInputComponent), multi: true } ] }) export class CustomInputComponent implements OnInit, ControlValueAccessor { constructor() { } private innerValue: any = '' ; private onTouchedCallback: () => void = function () { }; private onChangeCallback: (_: any) => void = function (_) { }; get value(): any { return this .innerValue; } set value(v: any) { if (v !== this .innerValue) { this .innerValue = v; this .onChangeCallback(v); } } /** * model view -> view value */ writeValue(value: any) { if (value !== this .innerValue) { this .innerValue = value; } } /** * view value ->model value */ registerOnChange(fn: any) { this .onChangeCallback = fn; } registerOnTouched(fn: any) { this .onTouchedCallback = fn; } } |
2 使用 get/set 关键字实现父子组件的双向绑定
其实实现双向绑定内部的本质原理就是父子组件的事件绑定机制。简单举例如下。
2.1 自定义子组件定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import { Input, Output, Component, EventEmitter } from '@angular/core' ; @Component({ selector: 'custom-input' , template: `<input [(ngModel)]= "innerValue" />`, }) export class CustomInputComponent { innerValue; @Input() get twoWayModel() { return this .innerValue; } set twoWayModel(val) { this .innerValue = val; this .twoWayModelChange.emit( this .innerValue); } @Output() twoWayModelChange: EventEmitter<string> = new EventEmitter(); } |
2.2 使用自定义组件
在需要使用组件的地方,通过 [(twoWayModel)]
即可实现双向绑定的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import { Input, Output } from '@angular/core' ; import { Component, forwardRef, Input } from '@angular/core' ; @Component({ selector: 'custom-input' , template: `<custom -input [(twoWayModel)]= "inputValue" (twoWayModelChange)= "onInputValueChange($event)" ></custom>` }) export class abcComponent { inputValue; onInputValueChange(val) { console.log(val); console.log(val === this .inputValue); // true } } |