Angular 带有 updateOn 模糊的自定义 FormControl
Angular custom FormControl with updateOn blur
我有一个反应式表单组,它与 updateOn submit 和 updateOn [=33= 一起工作得很好]变化.
但是当我切换到 updateOn blur 时,在我的自定义表单控件中输入一个字符后会直接触发所需的错误,而不会消失了。另外,当我提交我的表单时,我的自定义表单控件的所有值都是空的(即使字段已完成)。
我的表格TS:
activityForm = this.fb.group({
placeName: ['', { validators: [Validators.required, Validators.maxLength(75)] }],
description: ['', { validators: [Validators.required, Validators.minLength(25), Validators.maxLength(2000)] }],
}, { updateOn: 'blur' })
我的表格HTML:
<form class="container" [formGroup]="activityForm" (ngSubmit)="onSubmit()">
<div class="container__field">
<p class="container__title">{{'poi.place_name' | transloco}}</p>
<custom-input formControlName="placeName" placeholder="{{'poi.select_place_name' | transloco}}" [error]="activityForm.get('placeName')?.errors !== null && activityForm.get('placeName')!.touched"></custom-input>
<custom-error [text]="'error.required' | transloco" *ngIf="activityForm.get('placeName')?.hasError('required') && activityForm.get('placeName')?.touched"></custom-error>
<custom-error [text]="'error.maxlength' | transloco : { charact : this.activityForm.get('placeName')?.errors?.maxlength.requiredLength }" *ngIf="activityForm.get('placeName')?.hasError('maxlength')"></custom-error>
</div>
<div class="container__field">
<p class="container__title">{{'poi.description' | transloco}}</p>
<custom-textarea formControlName="description" placeholder="{{'poi.select_description' | transloco}}" [limit]="2000" [error]="activityForm.get('description')?.errors !== null && activityForm.get('description')!.touched"></custom-textarea>
<custom-error [text]="'error.required' | transloco" *ngIf="activityForm.get('description')?.hasError('required') && activityForm.get('description')?.touched"></custom-error>
<custom-error [text]="'error.maxlength' | transloco : { charact : this.activityForm.get('description')?.errors?.maxlength.requiredLength }" *ngIf="activityForm.get('description')?.hasError('maxlength')"></custom-error>
<custom-error [text]="'error.minlength' | transloco : { charact : this.activityForm.get('description')?.errors?.minlength.requiredLength }" *ngIf="activityForm.get('description')?.hasError('minlength')"></custom-error>
</div>
<div class="container__button">
<custom-button text="{{'poi.next_step' | transloco}}" color="primary" type="submit"></custom-button>
</div>
</form>
自定义FormControl TS(textarea之一):
export class CustomTextareaComponent implements ControlValueAccessor {
@Input() placeholder = '' // give a transloco string directly
@Input() limit = 500
@Input() error: boolean = false
value: string | null = null
currentNumberOfCharacters: number = 0
isActive: boolean | undefined
onChange: any = () => { }
onTouch: any = () => { }
touched = false
disabled = false
changes(event: Event) {
if (this.disabled) return
this.markAsTouched()
this.value = event?.target ? (event?.target as HTMLTextAreaElement).value : ''
this.currentNumberOfCharacters = this.value.length
this.onChange(this.value)
}
/* Methods needed by ControlValueAccessor to transform this component into a "form friendly" component */
registerOnChange(providedFunction: any) {
this.onChange = providedFunction
}
registerOnTouched(providedFunction: any) {
this.onTouch = providedFunction
}
writeValue(providedValue: any) {
if (providedValue) {
this.value = providedValue
this.currentNumberOfCharacters = providedValue.length
}
}
setDisabledState(providedDisabledVal: any) {
this.disabled = providedDisabledVal
}
markAsTouched() {
if (!this.touched) {
this.onTouch()
this.touched = true
}
}
}
Custom FormControl HTML (textarea one):
<div class="plnd-textarea">
<div class="plnd-textarea__field-container" [class.plnd-textarea__field-container--written]="value" [class.plnd-textarea__field-container--active]="isActive" [class.plnd-textarea__field-container--error]="error">
<textarea [placeholder]="placeholder | transloco" [value]="value" (keyup)="changes($event)" (focus)="isActive=true" (focusout)="isActive=false" [maxLength]="limit" class="plnd-textarea__field"></textarea>
</div>
<p class="plnd-textarea__characters">{{ currentNumberOfCharacters }}/{{ limit }} {{ 'common.characters' | transloco }}</p>
</div>
所以我的问题是:如何使 updateOn blur 与自定义 FormControl 一起工作?
您实际上错过了模糊事件。您的代码中似乎也有很多噪音,不知道例如 active
是做什么用的。这是清理后的版本:
模板:
<textarea [value]="value" (blur)="onBlur()" (keyup)="changes($event)"></textarea>
和 TS:
value: string | null = null;
onChange: any = () => {};
onTouch: any = () => {};
disabled = false;
changes(event: Event) {
if (this.disabled) return;
this.value = event?.target
? (event?.target as HTMLTextAreaElement).value
: '';
this.onChange(this.value);
}
onBlur() {
this.onTouch();
}
registerOnChange(providedFunction: any) {
this.onChange = providedFunction;
}
registerOnTouched(providedFunction: any) {
this.onTouch = providedFunction;
}
writeValue(providedValue: any) {
this.value = providedValue;
}
setDisabledState(providedDisabledVal: any) {
this.disabled = providedDisabledVal;
}
您还需要从您的 onChange 函数中删除 markAsTouched()
,否则您的错误将在输入时立即显示,因为该字段被触摸并且您对模糊进行了更新,这意味着该值不会更改直到模糊事件发生。
这里有一个DEMO供大家参考
也可以看看下面的文章,不要reinvent the wheel when implementing ControlValueAccessor
我有一个反应式表单组,它与 updateOn submit 和 updateOn [=33= 一起工作得很好]变化.
但是当我切换到 updateOn blur 时,在我的自定义表单控件中输入一个字符后会直接触发所需的错误,而不会消失了。另外,当我提交我的表单时,我的自定义表单控件的所有值都是空的(即使字段已完成)。
我的表格TS:
activityForm = this.fb.group({
placeName: ['', { validators: [Validators.required, Validators.maxLength(75)] }],
description: ['', { validators: [Validators.required, Validators.minLength(25), Validators.maxLength(2000)] }],
}, { updateOn: 'blur' })
我的表格HTML:
<form class="container" [formGroup]="activityForm" (ngSubmit)="onSubmit()">
<div class="container__field">
<p class="container__title">{{'poi.place_name' | transloco}}</p>
<custom-input formControlName="placeName" placeholder="{{'poi.select_place_name' | transloco}}" [error]="activityForm.get('placeName')?.errors !== null && activityForm.get('placeName')!.touched"></custom-input>
<custom-error [text]="'error.required' | transloco" *ngIf="activityForm.get('placeName')?.hasError('required') && activityForm.get('placeName')?.touched"></custom-error>
<custom-error [text]="'error.maxlength' | transloco : { charact : this.activityForm.get('placeName')?.errors?.maxlength.requiredLength }" *ngIf="activityForm.get('placeName')?.hasError('maxlength')"></custom-error>
</div>
<div class="container__field">
<p class="container__title">{{'poi.description' | transloco}}</p>
<custom-textarea formControlName="description" placeholder="{{'poi.select_description' | transloco}}" [limit]="2000" [error]="activityForm.get('description')?.errors !== null && activityForm.get('description')!.touched"></custom-textarea>
<custom-error [text]="'error.required' | transloco" *ngIf="activityForm.get('description')?.hasError('required') && activityForm.get('description')?.touched"></custom-error>
<custom-error [text]="'error.maxlength' | transloco : { charact : this.activityForm.get('description')?.errors?.maxlength.requiredLength }" *ngIf="activityForm.get('description')?.hasError('maxlength')"></custom-error>
<custom-error [text]="'error.minlength' | transloco : { charact : this.activityForm.get('description')?.errors?.minlength.requiredLength }" *ngIf="activityForm.get('description')?.hasError('minlength')"></custom-error>
</div>
<div class="container__button">
<custom-button text="{{'poi.next_step' | transloco}}" color="primary" type="submit"></custom-button>
</div>
</form>
自定义FormControl TS(textarea之一):
export class CustomTextareaComponent implements ControlValueAccessor {
@Input() placeholder = '' // give a transloco string directly
@Input() limit = 500
@Input() error: boolean = false
value: string | null = null
currentNumberOfCharacters: number = 0
isActive: boolean | undefined
onChange: any = () => { }
onTouch: any = () => { }
touched = false
disabled = false
changes(event: Event) {
if (this.disabled) return
this.markAsTouched()
this.value = event?.target ? (event?.target as HTMLTextAreaElement).value : ''
this.currentNumberOfCharacters = this.value.length
this.onChange(this.value)
}
/* Methods needed by ControlValueAccessor to transform this component into a "form friendly" component */
registerOnChange(providedFunction: any) {
this.onChange = providedFunction
}
registerOnTouched(providedFunction: any) {
this.onTouch = providedFunction
}
writeValue(providedValue: any) {
if (providedValue) {
this.value = providedValue
this.currentNumberOfCharacters = providedValue.length
}
}
setDisabledState(providedDisabledVal: any) {
this.disabled = providedDisabledVal
}
markAsTouched() {
if (!this.touched) {
this.onTouch()
this.touched = true
}
}
}
Custom FormControl HTML (textarea one):
<div class="plnd-textarea">
<div class="plnd-textarea__field-container" [class.plnd-textarea__field-container--written]="value" [class.plnd-textarea__field-container--active]="isActive" [class.plnd-textarea__field-container--error]="error">
<textarea [placeholder]="placeholder | transloco" [value]="value" (keyup)="changes($event)" (focus)="isActive=true" (focusout)="isActive=false" [maxLength]="limit" class="plnd-textarea__field"></textarea>
</div>
<p class="plnd-textarea__characters">{{ currentNumberOfCharacters }}/{{ limit }} {{ 'common.characters' | transloco }}</p>
</div>
所以我的问题是:如何使 updateOn blur 与自定义 FormControl 一起工作?
您实际上错过了模糊事件。您的代码中似乎也有很多噪音,不知道例如 active
是做什么用的。这是清理后的版本:
模板:
<textarea [value]="value" (blur)="onBlur()" (keyup)="changes($event)"></textarea>
和 TS:
value: string | null = null;
onChange: any = () => {};
onTouch: any = () => {};
disabled = false;
changes(event: Event) {
if (this.disabled) return;
this.value = event?.target
? (event?.target as HTMLTextAreaElement).value
: '';
this.onChange(this.value);
}
onBlur() {
this.onTouch();
}
registerOnChange(providedFunction: any) {
this.onChange = providedFunction;
}
registerOnTouched(providedFunction: any) {
this.onTouch = providedFunction;
}
writeValue(providedValue: any) {
this.value = providedValue;
}
setDisabledState(providedDisabledVal: any) {
this.disabled = providedDisabledVal;
}
您还需要从您的 onChange 函数中删除 markAsTouched()
,否则您的错误将在输入时立即显示,因为该字段被触摸并且您对模糊进行了更新,这意味着该值不会更改直到模糊事件发生。
这里有一个DEMO供大家参考
也可以看看下面的文章,不要reinvent the wheel when implementing ControlValueAccessor