1

Stackblitz https://stackblitz.com/edit/angular-mh4cox?embed=1&file=src/app/app.component.html

How do I properly show error messages on nested forms? Validation works btw

I tried so many methods here without any luck.

I want it to show an error message on html using ngIF (... invalid && ... touched )

Constructor

    constructor(private fb: FormBuilder,private http:HttpClient,private requestService:RequestService) {
    this.myForm = this.fb.group({
      id:[""],
      areaOfInterest: new FormControl("",Validators.required),
      status: new FormControl("",Validators.required),
      startDate: new FormControl("",Validators.required),
      endDate: new FormControl("",Validators.required),
      description: new FormControl("",Validators.required),
      notes: new FormControl("",Validators.required),
      createdBy:"",
      updatedBy:"",
      resourceDTOS: this.fb.array([]),
    });
    console.log(this.myForm);
    this.getOneRequest(localStorage.getItem('requestId'));
  }

This is the nested resource which is a FormArray in this case

addNewResourceDTOS() {
    this.control = <FormArray>this.myForm.controls.resourceDTOS;
    this.control.push(
        this.fb.group({
          seniority: this.fb.control(null,Validators.required),
          skillDTOS: this.fb.array([this.fb.group({
            skill: '' //i dont validate it here
          })]),
          resourceNotes: this.fb.control(null,Validators.required),
        })
    );
  }

The array skillDTOS

addNewResourceSkill(control) {
    control.push(
        this.fb.group({
          skill: new FormControl("",Validators.required),
        }))
  }

This is how I'm validating some of my main form variables

  get description() {
    return this.myForm.get('description');
  }
  get notes() {

    return this.myForm.get('notes');
  }

Example html "notes"

<small *ngIf="notes.invalid && notes.touched" class="text-danger">Please enter notes!</small>

It looks something like this

 data = {
    areaOfInterest:"",
    notes:"",

    resourceDTOS: [
      {
        seniority: "",
        skillDTOS: [
          {
            skill: "",
          }
        ],
        resourceNotes:""
      }
    ]
  }

Is it possible to validate at least seniority/resourceNotes (or skill at best) as well?

2
  • 1
    Sorry, the description is not clear. From your code, I see that the validation works completely correctly. Validation works for the seniority, resourceNotes, and skill fields (excluding first skill). Commented Nov 16, 2021 at 14:45
  • But my goal is to show error message when invalid. Is it possible? Like with the main form variables such as description Commented Nov 16, 2021 at 14:49

2 Answers 2

1

In your controller, you can define a new method that would check a particular field's validity based on its and its ancestor indeces. Here's an example for the skill field:

component.ts

  isSkillValid(resourceDTOSIndex: number, skillIndex: number): boolean {
    const resourceDTOSGroup = this.myForm.controls.resourceDTOS as FormGroup;
    const skillDTOSGroup =  resourceDTOSGroup.controls[resourceDTOSIndex] as FormGroup;
    const skillDTOS =  skillDTOSGroup.controls.skillDTOS as FormGroup;

    return skillDTOS.controls[skillIndex].valid;
  }; 

component.html

<input
  formControlName="skill"
  class="form-control"
  style="margin-right:5px;"
  type="text"
  placeholder="Enter skill"
  id="skill"
  name="skill"
  aria-label="button1"
  aria-describedby="button1"
/>

<div *ngIf="!isSkillValid(i, j)">
  The skill field has no valid value
</div>

P.S.

I would really suggest you refactor the component and split it into smaller pieces since it's already hard to read and manipulate over it.

Sign up to request clarification or add additional context in comments.

1 Comment

Note that this function will be called on each change detection and can affect performance. I'm especially careful with functions inside ngFor loops. Bumping into "infinite" loops (technically is not) sometimes, so just to be aware of the side effects of this.
1

You can simply access validators with hasError(). So just target the formcontrol you want, usually accessed by myFormgroup.get('formcontrolName')... So in case of your iteration of skill you can access the current formcontrol from the defined lines you have declared for each loop:

<div *ngFor="let lines of resource.get('skillDTOS').controls; let j=index">
  <div [formGroupName]="j">
    <input formControlName="skill"/>
    <div *ngIf="lines.get('skill').touched">
      <small *ngIf="lines.get('skill').hasError('required')">REQUIRED</small>
    </div>
  </div>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.