我使用 Control Value Accessor 接口在 Angular 中构建了一个共享输入组件。输入本身可以工作,但当我更改输入中的值时,验证不会更新。这是我创建的输入组件。
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormsModule,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
} from '@angular/forms';
@Component({
selector: 'app-input',
standalone: true,
imports: [FormsModule],
templateUrl: './input.component.html',
styleUrl: './input.component.scss',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => InputComponent),
multi: true,
},
],
})
export class InputComponent implements ControlValueAccessor, Validator {
@Input() value: string = '';
@Input() type: string = 'text';
@Input() placeholder: string = '';
@Input() disabled: boolean = false;
@Input() label: string = '';
// default to a random id unless one is provided.
@Input() id: string = '';
isInvalid: boolean = false;
touched: boolean = false;
onChange = (value: string) => {};
onTouched = () => {};
onValidationChange = () => {};
errorMessage: string = '';
writeValue(value: string): void {
this.value = value;
}
registerOnChange(onChange: any): void {
this.onChange = onChange;
}
registerOnTouched(onTouched: any): void {
this.onTouched = onTouched;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
onInputChange($event: Event) {
const input = $event.target as HTMLInputElement;
this.value = input.value;
this.onChange(this.value);
this.markAsTouched();
this.onValidationChange();
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
validate(control: AbstractControl): ValidationErrors | null {
this.errorMessage = this.getErrorMessage(control.errors);
return control.errors;
}
registerOnValidatorChange(onValidationChange: () => void): void {
this.onValidationChange = onValidationChange;
}
private getErrorMessage(errors: ValidationErrors | null): string {
if (!this.touched || !errors) {
return '';
}
if (errors['required']) {
return 'This field is required';
}
if (errors['minLength']) {
return `Minimum length is ${errors['minLength'].requiredLength}.`;
}
return '';
}
}
当我在验证方法中记录control.errors时,它仍然显示所需的验证仍然有错误。
我有一个示例角度项目在这里显示这个问题:
https://stackblitz.com/edit/stackblitz-starters-fy4zue?file=src%2Finput%2Finput.component.ts
我尝试过的事情:
我做错了什么,或者我忘记添加什么,或者这是一个实际的角度错误?
此场景不需要验证器,当您想在自定义组件中嵌入验证时可以使用它。
对于您的场景,您所需要做的就是调用
ngOnInit
和 onChange
上的函数,并确保使用以下方法获取 formControl
对象。
constructor(
private controlContainer: ControlContainer,
private elementRef: ElementRef
) {}
ngOnInit() {
const formControlName =
this.elementRef.nativeElement.getAttribute('formcontrolname');
console.log(this.controlContainer, formControlName);
this.control = this.controlContainer?.control?.get(formControlName) || null;
console.log(this.control);
this.errorMessage = this.getErrorMessage(this.control.errors);
}
然后在Change时,我们再次调用
getErrorMessage
。
onInputChange($event: Event) {
const input = $event.target as HTMLInputElement;
this.value = input.value;
console.log('asdf', this.value);
this.onChange(this.value);
this.errorMessage = this.getErrorMessage(this.control.errors);
this.markAsTouched();
// this.onValidationChange();
}
import {
Component,
ElementRef,
forwardRef,
Host,
inject,
Input,
OnInit,
Optional,
SkipSelf,
} from '@angular/core';
import {
AbstractControl,
ControlContainer,
ControlValueAccessor,
FormsModule,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
FormControl,
NgControl,
} from '@angular/forms';
@Component({
selector: 'app-input',
standalone: true,
imports: [FormsModule],
templateUrl: './input.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true,
},
],
})
export class InputComponent implements ControlValueAccessor {
value: string = '';
@Input() type: string = 'text';
@Input() placeholder: string = '';
@Input() disabled: boolean = false;
@Input() label: string = '';
// default to a random id unless one is provided.
@Input() id: string = '';
isInvalid: boolean = false;
touched: boolean = false;
onChange = (value: string) => {};
onTouched = () => {};
errorMessage: string = '';
control!: any;
constructor(
private controlContainer: ControlContainer,
private elementRef: ElementRef
) {}
ngOnInit() {
const formControlName =
this.elementRef.nativeElement.getAttribute('formcontrolname');
console.log(this.controlContainer, formControlName);
this.control = this.controlContainer?.control?.get(formControlName) || null;
console.log(this.control);
this.errorMessage = this.getErrorMessage(this.control.errors);
}
writeValue(value: string): void {
console.log(value);
this.value = value;
}
registerOnChange(onChange: any): void {
this.onChange = onChange;
}
registerOnTouched(onTouched: any): void {
this.onTouched = onTouched;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
onInputChange($event: Event) {
const input = $event.target as HTMLInputElement;
this.value = input.value;
console.log('asdf', this.value);
this.onChange(this.value);
this.errorMessage = this.getErrorMessage(this.control.errors);
this.markAsTouched();
// this.onValidationChange();
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
private getErrorMessage(errors: ValidationErrors | null): string {
if (errors?.['required']) {
return 'This field is required';
}
if (errors?.['minLength']) {
return `Minimum length is ${errors['minLength'].requiredLength}.`;
}
return '';
}
}