下面是详细讲解"Angular使用ControlValueAccessor创建自定义表单控件"的完整攻略。
概述
在 Angular 应用中,表单控件是很常见的组件,但是有时候我们需要自定义一些表单控件,如何实现呢?
Angular 提供了一种轻松自定义表单控件的方式,使用 ControlValueAccessor 接口。
ControlValueAccessor 是一个接口,它为我们自定义表单控件提供了规范和顺畅的用户体验。
接下来我们将介绍如何通过 ControlValueAccessor 接口来创建自定义表单控件。
步骤
第一步:定义自定义表单控件
我们需要定义一个自定义表单控件,这个控件需要实现 ControlValueAccessor 接口。
最简单的示例就是一个文本输入框组件。
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-custom-input',
template: `
<input type="text" [(ngModel)]="value">
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}]
})
export class CustomInputComponent implements ControlValueAccessor {
value: string;
onChange: (_: any) => {};
onTouched: () => {};
writeValue(value: any): void {
this.value = value;
this.onChange(this.value);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
// 控制是否被禁用
}
}
解释一下上述代码:
- @Component:指定组件自定义的选择器、模板和提供商。
- template:使用 ngModel 指令来创建一个文本输入框。
-
NG_VALUE_ACCESSOR:定义提供商,allowing the component to inject itself into the providers array. By doing this, we are allowing other components or directives to retrieve and access our component instances.
-
forwardRef:使用 forwardRef,我们可以解决在类中直接访问类名的问题。
-
multi:告诉 Angular 选择器中的 provider 是一个 token,可以有多个提供者。如果我们只有一个提供者,则可以省略 multi。
-
ControlValueAccessor 接口中的几个方法:
- writeValue:当表单控件的值发生变化时调用的方法。我们需要将传递进来的 value 值赋值给我们的 value 属性,并调用 onChange 方法。
- registerOnChange:注册一个回调方法,当表单控件的值发生变化时,调用这个方法。
- registerOnTouched:注册一个回调方法,当表单控件失去焦点时调用它。
- setDisabledState:为表单控件提供一个禁用的状态。这是可选的,如果你的表单控件不需要禁用状态,则可以忽略掉这个方法。
第二步:使用自定义表单控件
现在我们已经创建了一个自定义的表单控件,接下来让我们来使用它。
同样地,我们给出一个示例,一个使用自定义表单控件的表单组件。
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-form',
template: `
<form [formGroup]="form">
<app-custom-input formControlName="input"></app-custom-input>
</form>`
})
export class FormComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
input: ''
});
}
}
在上面的代码中我们引入了两个模块:
- FormBuilder:它会帮助我们创建表单模型的实例。
- FormGroup:它会创建一个组件实例,我们可以通过它来访问表单控件的值。
下面是表单控件的初始化:
- 在模板中使用 app-custom-input 组件。
- 在 ts 中初始化 FormGroup。注意,我们的表单控件名称必须与 formControlName 匹配。
至此,我们已经创建了一个自定义表单控件,并使用它来创建了一个表单组件。
示例
示例一:工资计算器
接下来我们通过一个工资计算器来示范实际应用 ControlValueAccessor。
工资计算器包含两个自定义控件组件:一个是输入工资的输入框,另一个是计算后的税后工资显示。
首先,我们来定义并实现输入工资的组件。
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-salary-input',
template: `
<input type="number" [(ngModel)]="value" (blur)="onTouched()">
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SalaryInputComponent),
multi: true
}]
})
export class SalaryInputComponent implements ControlValueAccessor {
value: number;
onChange: (_: any) => {};
onTouched: () => {};
writeValue(value: any): void {
this.value = value;
this.onChange(this.value);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
// 控制是否被禁用
}
}
注意,上述代码中使用了类型为 number 的 ngModel 指令。
接下来,让我们来定义计算后的税后工资显示组件。
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-net-income',
template: `
<div>{{value}}</div>
`
})
export class NetIncomeComponent {
@Input() value: number;
}
在这个组件中,我们使用了 @Input 装饰器将计算后的税后工资显示出来,没有使用 ControlValueAccessor 接口。
接下来是应用这两个组件的组件。
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-salary-calculator',
template: `
<form [formGroup]="form">
<app-salary-input formControlName="salary"></app-salary-input>
<app-net-income [value]="netIncome"></app-net-income>
</form>
`
})
export class SalaryCalculatorComponent {
form: FormGroup;
netIncome: number = 0;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
salary: ''
});
this.form.get('salary').valueChanges.subscribe((value) => {
const INCOME_TAX_RATE = 0.2; // 薪资税率
const INCOME_TAX_ACCUMULATOR = 3500; // 快递补贴
const taxBefore = value * INCOME_TAX_RATE;
const taxAfter = taxBefore - INCOME_TAX_ACCUMULATOR;
this.netIncome = value - taxAfter;
});
}
}
在这个组件中,我们使用了 FormBuilder 创建了一个 FormGroup,使用和之前创建自定义表单控件时一样的方式来初始化表单控件。
然后监听 salary 控制器值的变化,每当 salary 变化时,变化数值会经过计算后赋值给 netIncome。
示例二:星级评价
接下来我们再看一个自定义表单控件的示例:一个星级评价组件。
首先我们需要在模板中放置五个图片,用来表示评价星级。
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-star-rating',
template: `
<div>
<img *ngFor="let star of stars" [src]="starImg(star)" (click)="setValue(star)">
</div>
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => StarRatingComponent),
multi: true
}]
})
export class StarRatingComponent implements ControlValueAccessor {
stars = [1, 2, 3, 4, 5];
value: number;
onChange: (_: any) => {};
onTouched: () => {};
writeValue(value: any): void {
this.value = value;
this.onChange(this.value);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
// 控制是否被禁用
}
starImg(star: number): string {
return this.value >= star ? '../assets/star-filled.svg' : '../assets/star-empty.svg';
}
setValue(star: number) {
this.writeValue(star);
}
}
在这个组件中:
- 我们使用了 *ngFor 来遍历 stars 数组,生成五个 img 元素。这里的 img 是我们的评价星级。
- 用于值绑定的 [(ngModel)],是在 setValue 方法中赋值的。
然后我们来看看如何在组件中使用星级评价组件。
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-home',
template: `
<form [formGroup]="form">
<app-star-rating formControlName="rating"></app-star-rating>
</form>
`
})
export class HomeComponent {
form: FormGroup;
rating: number;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
rating: ''
});
this.form.get('rating').valueChanges.subscribe((value) => {
console.log(`You've selected ${value} star(s)`);
});
}
}
我们将自己定义的控件引入到模板中,并将它绑定到 formControlName 上。
最后我们还使用了 valueChanges 事件订阅器监听控件值的变化。
这就是如何通过 ControlValueAccessor 接口来创建自定义表单控件的详细攻略。希望对大家有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Angular使用ControlValueAccessor创建自定义表单控件 - Python技术站