ControlValueAccessor
是一个接口,它定义了一组方法,用于在 Angular 表单控件和模型之间进行双向数据绑定。这些方法包括writeValue
、registerOnChange
、registerOnTouched
和setDisabledState
。通过实现这些方法,我们可以创建自定义表单控件,并将其与 Angular 表单系统无缝集成。
例如,在项目中某个页面有多个这样的百分数输入框:输入框中的值除以100再传给后端,后端返回的值乘以100再显示
为避免每次都要重复处理数据,可以通过 ControlValueAccessor 实现一个自定义的表单输入框,具体实现方式如下:
<!-- html 中定义输入框 -->
<nz-input-group nzSize="small">
<div nz-row>
<div nz-col [nzSpan]="leftSpan">
<input nz-input type="text" nzSize="small" [disabled]="disabled" [ngModel]="percent" (ngModelChange)="onValueChange($event)"/>
</div>
<div nz-col nzSpan="{{ 24 - leftSpan }}" class="text-center">
<input nz-input type="text" nzSize="small" value="%" disabled />
</div>
</div>
</nz-input-group>
import { Component, OnInit, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { DecimalPipe } from '@angular/common';
export const EXE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PercentGroupComponent),
multi: true
};
@Component({
selector: 'app-percent-group',
templateUrl: './percent-group.component.html',
styleUrls: ['./percent-group.component.scss'],
providers: [EXE_VALUE_ACCESSOR, DecimalPipe]
})
export class PercentGroupComponent implements OnInit, ControlValueAccessor {
@Input() required = false;
@Input() leftSpan = 20;
public percent;
public disabled = false;
public onChange: (param: any) => {};
public onTouch: (param: any) => {};
constructor(
private decimalPipe: DecimalPipe
) { }
ngOnInit(): void {
}
/**
* 注册 onChange 事件, 将事件触发函数暂存,需要时通过调用 this.onChange 触发更新
* @param fn: 事件触发函数
* 数据流向:native form -> angular form
*/
public registerOnChange(fn: any): void {
this.onChange = fn;
}
/**
* 注册 onTouched 事件, 用户和控件交互时触发的回调函数,用于通知表单控件已经处于 touched 状态
* @param fn: 事件触发函数
* 数据流向:native form -> angular form
*/
public registerOnTouched(fn: any): void {
this.onTouch = fn;
}
/**
* input value change 事件
* @param value 值
*/
public onValueChange(value): void {
// 输入的值除以 100 再传回 angular form
if (value || value === 0) {
const num = new Decimal(Number(value)).div(100).toString();
this.onChange(num);
} else {
this.onChange(null);
}
}
/**
* 当angular form 控件禁用状态变更时调用此函数,以此控制本组件中控件的禁用状态
* 数据流向:angular form -> native form
*/
public setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
/**
* 将 angular form 传入的值写入本组件对应的控件中
* @param val: formControlName value
* 数据流向:angular form -> native form
*/
public writeValue(val): void {
this.percent = this.getPercentValue(val);
}
/**
* 处理百分数显示格式
* @param value: 后台返回的数值
*/
private getPercentValue(val) {
let percentStr;
const value = Number(val);
if ((value ?? -1) > -1) {
if (value === 0) {
percentStr = 0;
} else {
// 后台返回的数值先乘以100
const resVal = new Decimal(value).mul(100);
percentStr = resVal;
const amount = resVal.toString();
// 判断是否有四位小数,不足四位补0;若是整数,则保留两位小数
if (amount.includes('.')) {
// 小数点后面数字的长度
const n = amount.split('.')[1].length;
if (n < 5) {
percentStr = this.decimalPipe.transform(amount, '1.4');
}
} else {
percentStr = this.decimalPipe.transform(amount, '1.2');
}
}
}
return percentStr;
}
}