Angular ControlValueAccessor обязательный атрибут
У меня есть текстовое поле управления, который реализует ControlValueAccessor. В форме я поместил обязательный валидатор в имя поля формы:
this.formGroup = this.fb.group({
name: ['', Validators.required]
});
В шаблоне я использую элемент управления следующим образом:
<input-text formControlName="name"></input>
Проверка Angular обязательна, но внутреннее текстовое поле не имеет обязательного атрибута. Я хочу стилизовать это текстовое поле, если оно требуется, как мне это сделать?
3 ответа
2023 ответ
Вставьте элемент управления формой родительского компонента в пользовательский элемент управления, затем вызовитеhasValidator(...)
в родительском элементе управления следующим образом:
// your custom input component...
public get isRequired(): boolean {
return (
!!this.parent &&
!!this.parent.control &&
this.parent.control.hasValidator(Validators.required)
);
}
constructor(@Self() @Optional() private parent: NgControl) {
this.parent.valueAccessor = this;
}
// ...
Полный пример
В этом примере показано, как отображать звездочку при наличии необходимого валидатора.
Шаблон страницы регистрации:
<form [formGroup]="fg">
<app-text-input
[label]="'signup.name' | translate"
formControlName="name"
></app-text-input>
<app-text-input
[label]="'signup.email' | translate"
formControlName="email"
></app-text-input>
</form>
Компонент SignupPage:
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-signup',
templateUrl: './signup.page.html',
styleUrls: ['./signup.page.scss'],
})
export class SignupPage {
public readonly fg = new FormGroup({
name: new FormControl<string>('', [Validators.required]),
email: new FormControl<string>('', [Validators.required, Validators.email]),
});
}
Шаблон TextInput:
<input [formControl]="control" (blur)="onBlur()" />
<label *ngIf="label">
{{ label }}
<ng-container *ngIf="isRequired">*</ng-container>
</label>
Компонент TextInput:
import {
Component,
HostBinding,
Input,
OnDestroy,
OnInit,
Optional,
Self,
} from '@angular/core';
import {
ControlValueAccessor,
FormControl,
NgControl,
Validators,
} from '@angular/forms';
import { Subject, takeUntil, tap } from 'rxjs';
@Component({
selector: 'app-text-input',
templateUrl: './text-input.component.html',
styleUrls: ['./text-input.component.scss'],
})
export class TextInputComponent
implements OnInit, OnDestroy, ControlValueAccessor
{
@Input()
public label?: string;
public get isRequired(): boolean {
return (
!!this.parent &&
!!this.parent.control &&
this.parent.control.hasValidator(Validators.required)
);
}
public readonly control = new FormControl<string>('');
private onTouched?: () => void;
private onChanges?: (v: string | null) => void;
private readonly onDestroy$ = new Subject<void>();
constructor(@Self() @Optional() private parent: NgControl) {
this.parent.valueAccessor = this;
}
public ngOnInit(): void {
this.control.valueChanges
.pipe(
tap((e) => {
if (this.onChanges) {
this.onChanges(e);
}
}),
takeUntil(this.onDestroy$)
)
.subscribe();
}
public ngOnDestroy(): void {
this.onDestroy$.next();
this.onDestroy$.complete();
}
public registerOnChange(fn: any): void {
this.onChanges = fn;
}
public registerOnTouched(fn: any): void {
this.onTouched = fn;
}
public writeValue(value: string | null): void {
this.control.setValue(value, { emitEvent: false });
}
public setDisabledState(value: boolean): void {
if (value) {
this.control.disable();
} else {
this.control.enable();
}
}
public onBlur(): void {
if (this.onTouched) {
this.onTouched();
}
}
}
Вы должны проверить изменение значения элемента управления формы, как показано ниже
this.control.valueChanges.subscribe(
(res) => {
this.setErrorMessage(this.control);
}
)
А затем внутри setErrorMessage проверьте control.errors
if (control.errors) {
for (let propertyName in control.errors) {
if(propertyName == "required") {
// you can do things here to your input field by using element reference
}
}
}
У меня был создан, чтобы функционировать, чтобы знать, является ли поле ввода грязь = у или нет
//Validate single field
export function isInputFieldDirty(controlName: string, formGroupObject: FormGroup) {
if (formGroupObject.get(controlName)) {
return !formGroupObject.get(controlName).pristine &&
((formGroupObject.get(controlName).untouched && formGroupObject.get(controlName).dirty && formGroupObject.get(controlName).invalid) ||
(formGroupObject.get(controlName).touched && formGroupObject.get(controlName).invalid))
? true : false;
}
}
Если вы хотите стилизовать поле, если у контроллера есть требуемый валидатор, вы можете использовать эту утилиту:
isRequired(formControl: AbstractControl): boolean {
return this.hasRequiredField(formControl);
}
hasRequiredField = (abstractControl: AbstractControl): boolean => {
// caso formControl
if (abstractControl.validator) {
const validator = abstractControl.validator({} as AbstractControl);
if (validator && validator.required) {
return true;
}
} // caso formGroup
if (abstractControl['controls']) {
for (const controlName in abstractControl['controls']) {
if (abstractControl['controls'][controlName]) {
if (this.hasRequiredField(abstractControl['controls'][controlName])) {
return true;
}
}
}
}
return false;
}
в вашем контроллере
isRequired(formControlName){
isRequired(formControlName: string): boolean {
return this.utils.isRequired(this.form.get(formControlName));
}
}
если вы хотите показать сообщение, когда поле недопустимо, и применить стиль, вы можете использовать просто свойство "valid". Для отображения ошибки сообщения, когда поле недопустимо, объект возвращается в hasError('required')
Теперь, если вы хотите, чтобы стиль
<label> Name {{isRequired('name') ? '*' :'' }} </label>
<input-text formControlName="name" [ngClass]="{'required': isRequired('name'), 'notValid' : !this.form.get('name').valid }"></input>
<span class="help-block error" *ngIf="((form.get('name').touched || form.get('name').dirty) && !form.get('name').valid)">
<span *ngIf="form.get('name').hasError('required')">
{{ 'ERROR_MSG.REQUIRED' | translate }}
</span>
<span *ngIf="form.get('name').hasError('maxlength')">
{{ 'ERROR_MSG.MAXLENGTH' | translate }} {{getError('maxlength').requiredLength}}
</span>
<span *ngIf="form.get('name').hasError('minlength')">
{{ 'ERROR_MSG.MINLENGTH' | translate }} {{getError('minlength').requiredLength}}
</span>
<span *ngIf="form.get('name').hasError('myCustomError')">
{{ 'ERROR_MSG.CUSTOM' | translate }}
</span>
</span>
</div>
getError(error: string) {
return this.form.controls['name'].errors[error];
}