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

Viewed 1066

I am working on an Angular project using reactive form and I have the following doubts about form validation strategy. I will try to explain in details what I have done and what is my problem.

Into my component HTML code I put this form (that uses PrimeNG components):

<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>

Rendering something like this:

enter image description here

As you can see in the previous image and code snippet, because I have a lot of fields, I divided these fields into an accordion items (but this should be not a problem).

Then into my component TypeScript class I declared this FormGroup field (that I have injected into my constructor):

projectForm: FormGroup;

Then into the component ngOnInit() method I defined all the fields defined into the HTML form to retrieve the values inserted by the user in the form input fields:

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)]]

});

Finnally into my HTML I defined a button with an event to retrieve the values inserted by the user in my form, something like this:

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

at the moment the saveOrder() method only print the values of the compiled form:

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

It seems to works: clicking the Save button the saveOrder() method is call and I can see that the this.projectForm.value field contains the values inserted by the user into the form....untill now it seems to be all ok...

The problem is related to the validation rooles. As you can see at the moment I inserted the same fake rules to test it (then I will create specific validation for all my fields):

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

basically all the fields are requred and need have a minimum lenght of 5 characters. So I expected that if I don't compile a specific field or if I insert a value composed by a <5 character it have to go into error...but it is not so.

Infact, for example, if the user doesn't insert any value in my form and submit this form, I obtain no error. Simply the saveOrder() method will print into the console an object where all the fields are empty.

Why? What is wrong? What am I missing? How can I fix this issue?

3 Answers

I faced the same problem. I fixed it using

ngNativeValidate

Use it like below:-

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

Angular does a few things in the background when you use ReactiveForms.

Until all the fields have become valid, this.form.valid will be false. This is always the first thing to check while submitting the form. If this is false, don't go ahead with saving anything. If you add [disabled]="!form.valid" to the button, it will stay disabled, until all the validations pass.

Without filling anything in the form, just open up the Element Inspector and select the Control in which you have added the validations. You should see that it has added ng-invalid in the class attribute. You can use this to highlight the error fields using CSS.

For example:

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

The backdrop of this method is that it will show the error even when the user just lands in the page.

So normally another class ng-touched is used along with it.

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

The problem with it is that the ng-touched will only get attached when you visit the field at least once. To be able to use this, you can use a loop to go through all the controls and touch them programmatically.

One can also show an error message using a ngIf to show up only when there is an error.

For example:

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

This should be preferably used with a variable that gets set only on the form submit, so that the errors don't show up when the page loads for the first time.

You can take a look at the controls, as well as look into the form instance when submitting to get a more clearer picture of what's happening inside the form. You can find a lot of ways to do the same thing, but the solutions can onlly be determined by what behavior is exactly required.

That is the expected behavior the form is still invalid until it meets defined validation criteria, all you need to add your custom logic and display proper error message in template and prevent calling API until form gets valid.

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.
}

If you print console.log(this.projectForm.controls) you will see all controls with their properties you can notice the status of each form control i.e valid, touched, pristine, dirty read more in details.

And on based on each form control status you can show relevant error in template.

Related