[Angular] Implementing a ControlValueAccessor

本文介绍如何在Angular中创建自定义表单组件,并实现ControlValueAccessor接口来同步表单模型与视图值,同时支持表单验证。

So when you need to create a ControlValueAccessor?

 

When you want to use a custom component as form control. For example a counter component, you can custom the style and the way to control the value changes. 

 

It needs some setup for control value accessor, but after you have done it once, you can prettty much just copy & paste the same template around to create a control value access.

 

import { Component, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';


const COUNTER_VALUE_ACCESSOR = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => StockCounterComponent),
  multi: true
};

@Component({
  selector: 'stock-counter',
  providers: [COUNTER_VALUE_ACCESSOR],
  styleUrls: ['stock-counter.component.scss'],
  template: `
    ...
  `
})
export class StockCounterComponent implements ControlValueAccessor {

  value: number;

  writeValue(val: number) {
     this.value = val;
  }

  registerOnChange(fn: any) {
     this.onModelChange = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouch = fn;
  }

  ...
}

 

So the template is somehow like the code above, highly recommend to create any snippet or live template in your IDE. 

 

So inside the component we create, we have three methods, 

  • writeValue(obj: any)
  • registerOnChange(fn: Function)
  • registerOnTouched(fn: Function)

To understand each method is the key point to understand how control value access.

 

So first 'writeValue':

  value: number;

  writeValue(val: number) {
     this.value = val;
  }

This is for setting initial value, we take value from our form builder and pass to our component via 'writeValue'.

        <stock-counter
          [step]="10"
          [min]="10"
          [max]="1000"
          formControlName="quantity">

So the value comes from 'formControlName="quantity"'. 

 

Now at the point, you can think that our form holds a model value and our component also holds a view value.

We need to sync model value and view value.

 

The way to do that is by "registerOnChange":

  registerOnChange(fn: any) {
     this.onModelChange = fn;
  }
  increment() {
     this.value = this.value === this.max ?
       this.value :
       this.value + this.step;

     this.onModelChange(this.value);

  }

Every time our view value (component value) changed, to need to notify our form about the changes, we need to call 'this.onModelChange(this.value)' function and pass in the changes value.

After this form will be able to the updated value.

 

OK, now we able to sync the value from our component to our form. But you might think about this can be easily done by EventEmitter, when border to create Control value accessor? The most important reason is that "Validation"!

html "form" component actually does lots of thing underhook. What example, it set field state "untouch", "touched", "dirty", "prinstin". We use those status to do validation and error messages.

For example:

input.ng-touched.ng-invalid {
   border-left: 5px solid red;
}

If the field is touched and is invalid, we set the border to red. 

 

Currently, we have our value synced, and control value access also helps us to add form validation ability to our component. But once the value changed, our component still show 'ng-untouch':

 

So we need "registerOnTouched" function to help us to do that:

  registerOnTouched(fn: any) {
    this.onTouch = fn;
  }
  increment() {
     this.value = this.value === this.max ?
       this.value :
       this.value + this.step;

     this.onModelChange(this.value);
     this.onTouch();
  }

Now after the value changed, our component will be set 'ng-touched', now we are fully conver our component to a form component.

Code

 

转载于:https://www.cnblogs.com/Answer1215/p/6683667.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值