如何简化Angular响应式表单验证的冗余代码
简化Angular响应式表单的验证冗余代码
方法一:自定义状态指令自动处理样式类
把重复的valid & (dirty || touched)这类判断封装成指令,自动给表单控件容器添加对应的样式类,不用在每个控件里写冗长的绑定表达式。
指令实现(form-control-status.directive.ts)
import { Directive, ElementRef, OnInit, OnDestroy } from '@angular/core'; import { NgControl } from '@angular/forms'; import { Subscription } from 'rxjs'; @Directive({ selector: '[appFormControlStatus]' }) export class FormControlStatusDirective implements OnInit, OnDestroy { private statusSub?: Subscription; constructor( private el: ElementRef, private ngControl: NgControl ) {} ngOnInit(): void { this.statusSub = this.ngControl.control?.statusChanges?.subscribe(() => { this.updateClasses(); }); // 初始化时先执行一次状态判断 this.updateClasses(); } private updateClasses(): void { const control = this.ngControl.control; if (!control) return; const isDirtyOrTouched = control.dirty || control.touched; const isValid = control.valid && isDirtyOrTouched; const isInvalid = control.invalid && isDirtyOrTouched; this.el.nativeElement.classList.toggle('form-control--valid', isValid); this.el.nativeElement.classList.toggle('form-control--invalid', isInvalid); } ngOnDestroy(): void { this.statusSub?.unsubscribe(); } }
修改后的HTML
<form class="form" (ngSubmit)="onSubmit()" [formGroup]="form"> <div class="form__control form-control" appFormControlStatus> <label for="form_name" class="form-control__label">Your name</label> <div class="form-control__input-wrapper form-control__input-wrapper--marked"> <input type="text" class="form-control__input" id="form_name" name="name" formControlName="name"> </div> <div class="form-control__feedback" *ngIf="vm.name.errors && (vm.name.dirty || vm.name.touched)"> <p *ngIf="vm.name.errors.required">First name is required</p> </div> </div> <!-- 其他控件只需添加appFormControlStatus指令即可 --> <button class="btn btn-primary" type="submit" [disabled]='!form.valid'>Submit</button> </form>
方法二:封装可复用表单控件组件
把单个表单控件的完整结构(label、input、错误提示)封装成独立组件,彻底减少重复代码,还能统一所有控件的样式和逻辑。
组件实现(form-control.component.ts)
import { Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'app-form-control', template: ` <div class="form__control form-control" appFormControlStatus> <label [for]="id" class="form-control__label">{{ label }}</label> <div class="form-control__input-wrapper form-control__input-wrapper--marked"> <input [type]="type" class="form-control__input" [id]="id" [name]="controlName" [formControlName]="controlName"> </div> <div class="form-control__feedback" *ngIf="getControl().errors && (getControl().dirty || getControl().touched)"> <p *ngIf="getControl().errors.required">{{ requiredMessage || `${label} is required` }}</p> <!-- 可扩展其他错误提示类型 --> </div> </div> ` }) export class FormControlComponent { @Input() formGroup!: FormGroup; @Input() controlName!: string; @Input() label!: string; @Input() id!: string; @Input() type: string = 'text'; @Input() requiredMessage?: string; getControl() { return this.formGroup.get(this.controlName)!; } }
使用组件的HTML
<form class="form" (ngSubmit)="onSubmit()" [formGroup]="form"> <app-form-control [formGroup]="form" controlName="name" label="Your name" id="form_name" requiredMessage="First name is required" ></app-form-control> <!-- 其他控件直接复用组件,传入对应参数即可 --> <button class="btn btn-primary" type="submit" [disabled]='!form.valid'>Submit</button> </form>
方法三:用管道简化错误显示判断
如果不想封装组件,也可以写个轻量管道快速判断是否显示某个错误:
管道实现(validation-error.pipe.ts)
import { Pipe, PipeTransform } from '@angular/core'; import { AbstractControl } from '@angular/forms'; @Pipe({ name: 'hasError' }) export class ValidationErrorPipe implements PipeTransform { transform(control: AbstractControl, errorKey: string): boolean { return control.hasError(errorKey) && (control.dirty || control.touched); } }
修改后的错误提示HTML
<div class="form-control__feedback"> <p *ngIf="vm.name | hasError: 'required'">First name is required</p> </div>
内容的提问来源于stack exchange,提问作者Sergino
相关产品推荐
相关产品推荐

