MST

星途 面试题库

面试题:Angular响应式表单中如何处理复杂的嵌套表单结构及验证

设想你正在开发一个电商订单管理系统,订单表单包含收货地址、商品列表等多个嵌套部分,每个部分都有各自的验证规则,如地址格式验证、商品数量必须为正整数等。请阐述如何在Angular响应式表单中构建这样复杂的嵌套表单结构,并实现相应的验证逻辑,包括自定义验证器的编写和使用,以及如何在提交表单时统一处理所有验证结果。
41.0万 热度难度
前端开发Angular

知识考点

AI 面试

面试题答案

一键面试
  1. 构建嵌套表单结构
    • 在组件类中,使用FormGroupFormArray来构建嵌套表单。
    • 例如,假设订单表单包含收货地址和商品列表:
    import { Component } from '@angular/core';
    import { FormGroup, FormControl, FormArray, Validators } from '@angular/forms';
    
    @Component({
      selector: 'app - order - form',
      templateUrl: './order - form.component.html',
      styleUrls: ['./order - form.component.css']
    })
    export class OrderFormComponent {
      orderForm: FormGroup;
    
      constructor() {
        this.orderForm = new FormGroup({
          shippingAddress: new FormGroup({
            street: new FormControl('', Validators.required),
            city: new FormControl('', Validators.required),
            zipCode: new FormControl('', [Validators.required, Validators.pattern('^[0 - 9]{5}$')])
          }),
          productList: new FormArray([])
        });
      }
    
      get productList() {
        return this.orderForm.get('productList') as FormArray;
      }
    
      addProduct() {
        const productForm = new FormGroup({
          productName: new FormControl('', Validators.required),
          quantity: new FormControl(1, [Validators.required, Validators.min(1)])
        });
        this.productList.push(productForm);
      }
    }
    
  2. 自定义验证器编写
    • 自定义验证器是一个函数,返回ValidationErrors | null
    • 例如,自定义一个验证邮编格式的验证器:
    import { AbstractControl, ValidationErrors } from '@angular/forms';
    
    export function zipCodeValidator(control: AbstractControl): ValidationErrors | null {
      const zipCode = control.value;
      if (!zipCode) {
        return null;
      }
      const valid = /^[0 - 9]{5}$/.test(zipCode);
      return valid? null : { invalidZipCode: true };
    }
    
    • 使用自定义验证器:
    zipCode: new FormControl('', [Validators.required, zipCodeValidator])
    
  3. 提交表单时统一处理验证结果
    • 在模板中,绑定(ngSubmit)事件到组件类中的提交方法。
    • 在提交方法中,检查orderForm.valid。如果表单有效,处理表单数据;如果无效,可遍历表单控件获取错误信息。
    <form [formGroup]="orderForm" (ngSubmit)="onSubmit()">
      <!-- 收货地址部分 -->
      <div formGroupName="shippingAddress">
        <label for="street">Street:</label>
        <input type="text" id="street" formControlName="street">
        <label for="city">City:</label>
        <input type="text" id="city" formControlName="city">
        <label for="zipCode">Zip Code:</label>
        <input type="text" id="zipCode" formControlName="zipCode">
        <div *ngIf="orderForm.get('shippingAddress.zipCode').hasError('invalidZipCode') && (orderForm.get('shippingAddress.zipCode').touched || orderForm.get('shippingAddress.zipCode').dirty)">
          Invalid zip code format.
        </div>
      </div>
      <!-- 商品列表部分 -->
      <div formArrayName="productList">
        <div *ngFor="let product of productList.controls; let i = index" [formGroupName]="i">
          <label for="productName{{i}}">Product Name:</label>
          <input type="text" [id]="'productName' + i" formControlName="productName">
          <label for="quantity{{i}}">Quantity:</label>
          <input type="number" [id]="'quantity' + i" formControlName="quantity">
        </div>
        <button type="button" (click)="addProduct()">Add Product</button>
      </div>
      <button type="submit">Submit</button>
    </form>
    
    onSubmit() {
      if (this.orderForm.valid) {
        const formData = this.orderForm.value;
        // 处理表单数据,如发送到服务器
        console.log('Form submitted successfully:', formData);
      } else {
        // 遍历表单获取错误信息
        Object.keys(this.orderForm.controls).forEach(field => {
          const control = this.orderForm.get(field);
          if (control instanceof FormGroup) {
            Object.keys(control.controls).forEach(subField => {
              const subControl = control.get(subField);
              if (subControl && subControl.errors) {
                console.log(`${field}.${subField} has errors:`, subControl.errors);
              }
            });
          } else if (control instanceof FormArray) {
            control.controls.forEach((formGroup, index) => {
              Object.keys(formGroup.controls).forEach(subField => {
                const subControl = formGroup.get(subField);
                if (subControl && subControl.errors) {
                  console.log(`productList[${index}].${subField} has errors:`, subControl.errors);
                }
              });
            });
          } else if (control && control.errors) {
            console.log(`${field} has errors:`, control.errors);
          }
        });
      }
    }