You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular表单:FormBuilder实现必填字段校验提示失效求助

Angular表单必填提示不显示的修复方案

你的代码存在几个关键问题,导致必填提示无法正常渲染,以下是具体问题和修复方案:

核心问题分析

  1. 表单结构混乱:同时定义了独立的credit FormGroup和profileForm内部的credit分组,且ngOnInit还重新覆盖了credit变量,但模板绑定的是profileForm,导致你在getter里获取的montantDuPret和实际表单里的控件完全无关。
  2. FormArray使用错误:aliases被定义为单个FormControl的数组,但你需要的是每个贷款条目对应一个完整的FormGroup(包含typeDePret、montantDuPret等字段),当前结构完全不符合需求。
  3. 控件绑定错误:模板里的输入框同时设置了[formControlName]="i"和formControlName="montantDuPret",属性重复且逻辑错误;另外FormArray没有嵌套FormGroup,导致无法正确绑定多个字段。
  4. 验证器配置错误:montantDuPret的验证器写法错误(初始值位置放了验证器),还错误地用了邮箱正则匹配金额字段,完全不符合场景。
  5. 校验状态判断错误:模板里的*ngIf判断的是独立credit里的控件状态,不是当前FormArray条目中的控件,自然不会触发提示。

修复后的代码

TypeScript代码

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators, FormArray, FormBuilder} from '@angular/forms';

@Component({
  selector: 'app-chargement-du-questionnaire',
  templateUrl: './chargement-du-questionnaire.component.html',
  styleUrls: ['./chargement-du-questionnaire.component.css']
})
export class ChargementDuQuestionnaireComponent {
  errorMessage: string = "";
  profileForm: FormGroup;

  constructor(private fb: FormBuilder) {
    // 初始化表单,aliases是FormGroup的数组
    this.profileForm = this.fb.group({
      aliases: this.fb.array([
        this.createCreditFormGroup() // 初始化第一个贷款条目
      ])
    });
  }

  // 创建单个贷款的FormGroup,包含所有字段和验证规则
  createCreditFormGroup(): FormGroup {
    return this.fb.group({
      typeDePret: ['', Validators.required],
      montantDuPret: ['', [Validators.required, Validators.pattern('^[0-9]+$')]], // 金额用数字正则
      tauxDuPret: ['', Validators.required],
      dureeDuPretAn: ['', Validators.required],
      dureeDuPretMois: ['', Validators.required],
      dontDiffere: ['', Validators.required]
    });
  }

  // 获取aliases FormArray
  get aliasArray() {
    return this.profileForm.get('aliases') as FormArray;
  }

  // 获取指定索引的贷款FormGroup
  getCreditFormGroup(index: number): FormGroup {
    return this.aliasArray.at(index) as FormGroup;
  }

  addAlias() {
    if (this.aliasArray.length === 3) {
      this.errorMessage = "最多添加3个贷款";
      setTimeout(() => {
        window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
      }, 10);
    } else {
      this.errorMessage = "";
      this.aliasArray.push(this.createCreditFormGroup());
      setTimeout(() => {
        window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
      }, 10);
    }
  }

  removeAlias(index: number) {
    if (this.aliasArray.length === 3) {
      this.errorMessage = "";
    }
    this.aliasArray.removeAt(index);
  }
}

HTML代码

<form [formGroup]="profileForm" class="needs-validation" novalidate>
  <div formArrayName="aliases">
    <div class="form" *ngFor="let alias of aliasArray.controls; let i=index" [formGroupName]="i">
      <p> Pret n°{{i+1}}</p> 

      <div>
        <label for="typeDePret-{{i}}">Type de prêt:</label><br>
        <select class="form-select" aria-label="Default select example" id="typeDePret-{{i}}" formControlName="typeDePret">
          <option value="">请选择贷款类型</option>
          <option value="1">Prêt classique (amortissable)</option>
          <option value="2">In fine</option>
          <option value="3">Prêt à taux Zéro</option>
          <option value="4">Prêt relais</option>
        </select>
        <div class="invalid-feedback" *ngIf="getCreditFormGroup(i).get('typeDePret')?.invalid && getCreditFormGroup(i).get('typeDePret')?.touched">
          请选择贷款类型
        </div>
      </div>

      <label for="montantDuPret-{{i}}">Montant du prêt</label><br>
      <div class="input-group mb-3">
        <input id="montantDuPret-{{i}}" type="text" formControlName="montantDuPret" class="form-control" aria-label="Amount">
        <span class="input-group-text">.00 €</span> 
        <div class="invalid-feedback" *ngIf="getCreditFormGroup(i).get('montantDuPret')?.invalid && getCreditFormGroup(i).get('montantDuPret')?.touched">
          <div *ngIf="getCreditFormGroup(i).get('montantDuPret')?.errors?.['required']">
            请输入贷款金额
          </div>
          <div *ngIf="getCreditFormGroup(i).get('montantDuPret')?.errors?.['pattern']">
            金额必须为数字
          </div>
        </div>
      </div>
                    
      <label for="tauxDuPret-{{i}}">Taux du prêt</label><br>
      <div class="input-group mb-3">
        <input id="tauxDuPret-{{i}}" type="text" formControlName="tauxDuPret" class="form-control" aria-label="Amount">
        <span class="input-group-text">%</span>  
        <div class="invalid-feedback" *ngIf="getCreditFormGroup(i).get('tauxDuPret')?.invalid && getCreditFormGroup(i).get('tauxDuPret')?.touched">
          请输入贷款利率
        </div>
      </div>

      <label for="dureeDuPret-{{i}}">Durée totale</label><br>
      <div class="input-group mb-3">
        <input id="dureeDuPretAn-{{i}}" type="text" formControlName="dureeDuPretAn" class="form-control" aria-label="Years">
        <span class="input-group-text">an(s)</span>
        <input id="dureeDuPretMois-{{i}}" type="text" formControlName="dureeDuPretMois" class="form-control" aria-label="Months">
        <span class="input-group-text">mois</span>   
        <div class="invalid-feedback" *ngIf="getCreditFormGroup(i).get('dureeDuPretAn')?.invalid && getCreditFormGroup(i).get('dureeDuPretAn')?.touched">
          请输入年数
        </div>
        <div class="invalid-feedback" *ngIf="getCreditFormGroup(i).get('dureeDuPretMois')?.invalid && getCreditFormGroup(i).get('dureeDuPretMois')?.touched">
          请输入月数
        </div>
      </div>

      <label for="dontDiffere-{{i}}">Dont différé</label><br>
      <select class="form-select" aria-label="Default select example" id="dontDiffere{{i}}" formControlName="dontDiffere">
        <option value="">请选择延期时长</option>
        <option value="1">1 mois</option>
        <option value="2">2 mois</option>
        <option value="3">3 mois</option>
        <option value="4">4 mois</option>
        <option value="5">5 mois</option>
        <option value="6">6 mois</option>
        <option value="7">7 mois</option>
        <option value="8">8 mois</option>
        <option value="9">9 mois</option>
        <option value="10">10 mois</option>
        <option value="11">11 mois</option>
        <option value="12">12 mois</option>
        <option value="13">13 mois</option>
        <option value="14">14 mois</option>
        <option value="15">15 mois</option>
        <option value="16">16 mois</option>
        <option value="17">17 mois</option>
        <option value="18">18 mois</option>
        <option value="19">19 mois</option>
        <option value="20">20 mois</option>
        <option value="21">21 mois</option>
        <option value="22">22 mois</option>
        <option value="23">23 mois</option>
        <option value="24">24 mois</option>
      </select> 
      <div class="invalid-feedback" *ngIf="getCreditFormGroup(i).get('dontDiffere')?.invalid && getCreditFormGroup(i).get('dontDiffere')?.touched">
        请选择延期时长
      </div>

      <button (click)="removeAlias(i)">删除此贷款</button>
    </div>
  </div>

  <div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
  <button type="button" (click)="addAlias()">+ 添加另一个贷款</button>
</form>

修复说明

  • 重构了表单结构,aliases改为FormGroup数组,每个条目对应完整的贷款字段
  • 统一使用profileForm作为唯一表单实例,删除了冗余的独立credit变量
  • 修正了验证器的写法,给金额字段替换了数字正则,移除了错误的邮箱验证
  • 模板中使用[formGroupName]="i"绑定FormArray中的每个FormGroup,确保字段绑定正确
  • 通过getCreditFormGroup(index)方法获取对应条目的控件,正确判断校验状态
  • 给所有必填字段添加了空选项,确保用户选择时能触发校验

内容的提问来源于stack exchange,提问作者Abate Raphael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 22:55:19