为什么验证器规则在我的 Angular 项目中使用这个反应式表单示例似乎不起作用?

Why Validators rules seems not work using this reactive form example in my Angular project?

我正在使用 反应式表单 进行一个 Angular 项目,我对表单验证策略有以下疑问。我会尝试详细解释我做了什么以及我的问题是什么。

进入我的组件 HTML 代码,我将此表单(使用 PrimeNG 组件)放入:

<form [formGroup]="projectForm">
    <p-accordion [multiple]="true">
      <p-accordionTab header="Informazioni generali ordine">
        <div id="informazioni_generale_ordine">
          <div class="row">
            <div class="col-2">
              <p>ID Ordine</p>
            </div>
            <div class="col-10">
              <p-inputNumber id="idOrdine" formControlName="idOrdine"></p-inputNumber>
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Data inserimento ordine</p>
            </div>
            <div class="col-10">
              <p-calendar id="dataInserimentoOrdine" formControlName="dataInserimentoOrdine"></p-calendar>
            </div>
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Stato ordine</p>
          </div>
          <div class="col-10">
            <input id="statoOrdine" formControlName="statoOrdine" type="text" pInputText />
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Commessa</p>
          </div>
          <div class="col-10">
            <input id="commessa" formControlName="commessa" type="text" pInputText />
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>CIG</p>
          </div>
          <div class="col-10">
            <input id="CIG" formControlName="CIG" type="text" pInputText />
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Data inizio attività</p>
          </div>
          <div class="col-10">
            <p-calendar id="dataInizioAttivita" formControlName="dataInizioAttivita"></p-calendar>
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Data fine attività</p>
          </div>
          <div class="col-10">
            <p-calendar id="dataFineAttivita" formControlName="dataFineAttivita"></p-calendar>
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Referente</p>
          </div>
          <div class="col-10">
            <input id="referente" formControlName="referente" type="text" pInputText />
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Ruolo referente</p>
          </div>
          <div class="col-10">
            <input id="ruoloReferente" formControlName="ruoloReferente" type="text" pInputText />
          </div>
        </div>

        <div class="row">
          <div class="col-2">
            <p>Tipologia di partecipazione</p>
          </div>
          <div class="col-10">
            <input id="tipologiaDiPartecipazione" formControlName="tipologiaDiPartecipazione" type="text" pInputText />
          </div>
        </div>


        <div class="row">
          <div class="col-2">
            <p>Quota percentuale di RTI</p>
          </div>
          <div class="col-10">
            <p-inputNumber id="quotaPercentualeDiRTI" formControlName="quotaPercentualeDiRTI"  suffix="%"></p-inputNumber>
          </div>
        </div>


      </p-accordionTab>
      <p-accordionTab header="Informazioni cliente">
        <div id="informazioni_cliente">

          <div class="row">
            <div class="col-2">
              <p>Cliente</p>
            </div>
            <div class="col-10">
              <input id="cliente" formControlName="cliente" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Partita IVA cliente</p>
            </div>
            <div class="col-10">
              <input id="vatCliente" formControlName="vatCliente" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Cliente finale</p>
            </div>
            <div class="col-10">
              <input id="clienteFinale" formControlName="clienteFinale" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Partita IVA cliente finale</p>
            </div>
            <div class="col-10">
              <input id="vatClienteFinale" formControlName="vatClienteFinale" type="text" pInputText />
            </div>
          </div>

        </div>
      </p-accordionTab>

      <p-accordionTab header="Informazioni contratto">
        <div id="informazioni_contratto">
          <div class="row">
            <div class="col-2">
              <p>Tipologia contratto</p>
            </div>
            <div class="col-10">
              <input id="tipologiaContratto" formControlName="tipologiaContratto" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Importo contratto</p>
            </div>
            <div class="col-10">
              <p-inputNumber id="importoContratto" formControlName="importoContratto"  mode="currency" currency="EUR" locale="de-DE"></p-inputNumber>
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Link Contratto</p>
            </div>
            <div class="col-10">
              <input id="linkContratto" formControlName="linkContratto" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Data sottoscrizione contratto</p>
            </div>
            <div class="col-10">
              <p-calendar id="dataSottoscrizioneContratto" formControlName="dataSottoscrizioneContratto"></p-calendar>
            </div>
          </div>
        </div>
      </p-accordionTab>

      <p-accordionTab header="Informazioni società">
        <div id="informazioni_societa">
          <div class="row">
            <div class="col-2">
              <p>Nome Società</p>
            </div>
            <div class="col-10">
              <input id="nomeSocieta" formControlName="nomeSocieta" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Partita IVA Società</p>
            </div>
            <div class="col-10">
              <input id="vatSocieta" formControlName="vatSocieta" type="text" pInputText/>
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>BU</p>
            </div>
            <div class="col-10">
              <input id="bu" formControlName="bu" type="text" pInputText />
            </div>
          </div>
        </div>
      </p-accordionTab>

      <p-accordionTab header="Informazioni accordo quadro">
        <div id="informazioni_accordo_quadro">
          <div class="row">
            <div class="col-2">
              <p>Presenza accordo quadro</p>
            </div>
            <div class="col-10">
              <p-selectButton [options]="presenzaAQOption" id="presenzaAQ" formControlName="presenzaAQ" ></p-selectButton>
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Link identificatovo accordo quadro</p>
            </div>
            <div class="col-10">
              <input id="linkIdentificativoAQ" formControlName="linkIdentificativoAQ" type="text" pInputText />
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Accordo quadro</p>
            </div>
            <div class="col-10">
              <p-inputNumber id="accordoQaudro" formControlName="accordoQaudro" mode="currency" currency="EUR" locale="de-DE" ></p-inputNumber>
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Residuo accordo quadro</p>
            </div>
            <div class="col-10">
              <p-inputNumber  id="residuoAccordoQaudro" formControlName="residuoAccordoQaudro" mode="currency" currency="EUR" locale="de-DE"></p-inputNumber>
            </div>
          </div>

          <div class="row">
            <div class="col-2">
              <p>Compagine di accordo quadro</p>
            </div>
            <div class="col-10">
              <input id="compagineDiAQ" formControlName="compagineDiAQ" type="text" pInputText />
            </div>
          </div>


        </div>
    </p-accordionTab>
    </p-accordion>
</form>

渲染如下:

正如你在上图和代码片段中看到的,因为我有很多字段,所以我将这些字段分成了一个手风琴项目(但这应该不是问题)。

然后到我的组件 TypeScript class 我声明了这个 FormGroup 字段(我已经注入到我的构造函数中):

projectForm: FormGroup;

然后进入组件ngOnInit()方法我定义了定义到HTML表单中的所有字段来检索用户在表单输入字段中插入的值:

ngOnInit() {

  this.projectForm = this.fb.group({
  idOrdine: [null, [Validators.required, Validators.minLength(5)]],
  dataInserimentoOrdine: [null, [Validators.required, Validators.minLength(5)]],
  statoOrdine: [null, [Validators.required, Validators.minLength(5)]],
  commessa: [null, [Validators.required, Validators.minLength(5)]],
  CIG: [null, [Validators.required, Validators.minLength(5)]],
  dataInizioAttivita: [null, [Validators.required, Validators.minLength(5)]],
  dataFineAttivita: [null, [Validators.required, Validators.minLength(5)]],
  referente: [null, [Validators.required, Validators.minLength(5)]],
  ruoloReferente: [null, [Validators.required, Validators.minLength(5)]],
  tipologiaDiPartecipazione: [null, [Validators.required, Validators.minLength(5)]],
  quotaPercentualeDiRTI: [null, [Validators.required, Validators.minLength(5)]],

  cliente: [null, [Validators.required, Validators.minLength(5)]],
  vatCliente: [null, [Validators.required, Validators.minLength(5)]],
  clienteFinale: [null, [Validators.required, Validators.minLength(5)]],
  vatClienteFinale: [null, [Validators.required, Validators.minLength(5)]],

  tipologiaContratto: [null, [Validators.required, Validators.minLength(5)]],
  importoContratto: [null, [Validators.required, Validators.minLength(5)]],
  linkContratto: [null, [Validators.required, Validators.minLength(5)]],
  dataSottoscrizioneContratto: [null, [Validators.required, Validators.minLength(5)]],

  nomeSocieta: [null, [Validators.required, Validators.minLength(5)]],
  vatSocieta: [null, [Validators.required, Validators.minLength(5)]],
  bu: [null, [Validators.required, Validators.minLength(5)]],

  presenzaAQ: [null, [Validators.required, Validators.minLength(5)]],
  linkIdentificativoAQ: [null, [Validators.required, Validators.minLength(5)]],
  accordoQaudro: [null, [Validators.required, Validators.minLength(5)]],
  residuoAccordoQaudro: [null, [Validators.required, Validators.minLength(5)]],
  compagineDiAQ: [null, [Validators.required, Validators.minLength(5)]]

});

最后进入我的 HTML 我定义了一个带有事件的按钮来检索用户在我的表单中插入的值,如下所示:

<button pButton type="button" label="Save" icon="pi pi-check" (click)="saveOrder($event)"></button>

目前 saveOrder() 方法仅打印已编译表单的值:

public saveOrder(event) {
    console.log("saveOrder() START");
    console.log(this.projectForm.value);
}

它似乎有效:单击 Save 按钮调用 saveOrder() 方法,我可以看到 this.projectForm.value 字段包含用户插入到表单中的值....直到现在它似乎一切正常...

问题与验证角色有关。正如你现在所看到的,我插入了相同的假规则来测试它(然后我将为我的所有字段创建特定的验证):

[Validators.required, Validators.minLength(5)]

基本上所有字段都是必填项,并且至少需要 5 个字符的长度。所以我预计如果我不编译一个特定的字段或者如果我插入一个由 <5 字符组成的值它必须出错......但事实并非如此。

事实上,例如,如果用户没有在我的表单中插入任何值并提交此表单,我就不会收到任何错误。 saveOrder() 方法将在控制台中打印一个所有字段都为空的对象。

为什么?怎么了?我错过了什么?我该如何解决这个问题?

我遇到了同样的问题。我使用

修复了它
ngNativeValidate

像下面这样使用它:-

<form ngNativeValidate [formGroup]="form" (ngSubmit)="onSubmit()">

Angular 当您使用 ReactiveForms 时,会在后台做一些事情。

在所有字段都变为有效之前,this.form.valid 将为 false。这始终是提交表单时要检查的第一件事。如果这是错误的,请不要继续保存任何内容。如果您将 [disabled]="!form.valid" 添加到按钮,它将保持禁用状态,直到所有验证都通过。

无需在表单中填写任何内容,只需打开元素检查器和 select 您已在其中添加验证的控件。您应该看到它在 class 属性中添加了 ng-invalid。您可以使用它来使用 CSS.

突出显示错误字段

例如:

 .ng-invalid { border: 1px solid red }

这种方法的背景是,即使用户刚登陆页面,它也会显示错误。

所以通常另一个 class ng-touched 与它一起使用。

.ng-touched.ng-invalid { ... }

它的问题是 ng-touched 只会在您至少访问该字段一次时附加。为了能够使用它,您可以使用循环遍历所有控件并以编程方式触摸它们。

也可以使用 ngIf 仅在出现错误时显示错误消息。

例如:

<ng-container *ngIf="form.controls.get('idOrdine').errors">Error message</ng-container>

这最好与仅在表单提交时设置的变量一起使用,这样错误就不会在页面首次加载时出现。

您可以查看控件,也可以在提交时查看表单实例,以便更清楚地了解表单内部发生的情况。您可以找到很多方法来做同样的事情,但解决方案只能根据确切需要的行为来确定。

这是预期的行为,表单在满足定义的验证标准之前仍然无效,所有您需要添加自定义逻辑并在模板中显示正确的错误消息并阻止调用 API 直到表单有效。

public saveOrder() {
  // if form is invalid do not do any action.
  if (this.projectForm.invalid) {
     return
  }

 // call api or whatever your logic once form is submitted with valid data.
}

如果您打印 console.log(this.projectForm.controls),您将看到所有控件及其属性,您可以注意到每个表单控件的状态,即 validtouchedpristinedirty read more 详情。

并且根据每个表单控件状态,您可以显示相关的 error in template