1

I'm building a form array correctly via this way:

this.items.forEach(element => {
                    (<FormArray>this.myFormFixtures.get('fixtures')).push(this.fBuilder.group({
                        id: [element.id],
                        name: [element.name, Validators.required],
                        description: [element.description],
                        startDateTime: this.buildTimeDetails(element.startDateTime),
                        venueId: [element.venueId, Validators.required],
                        participants: this.buildParticipants(element.participants)
                    }));
                });
                this.myFormFixtures.valueChanges
                    .subscribe(formData => this.checkFixturesFormValidity(formData));

The "Participants" key is generated into an array with 2 ids with null value like so:

[
{id: null},
{id: null}
]

By default the 2 ids are optional. But I want to set both participant ids formControlNames to be required should one acquire a value.

So far my "checkFixturesFormValidity" function is looking like this, but for some reason the validation isn't being set:

checkFixturesFormValidity(formData)
    {
        console.log(formData);

        const control = <FormArray>this.myFormFixtures.controls['fixtures'];

        for (let i = 0; i < control.length; i++) {

            const participantsControl = (<FormArray>this.myFormFixtures.controls['fixtures']).at(i).get('participants') as FormArray;

                if ((((<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(0).get('id').value) != null) || (((<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(1).get('id').value) != null))
                {
                    (<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(0).get('id').setValidators([Validators.required]);
                    (<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(1).get('id').setValidators([Validators.required]);
                }


        }

    }
8
  • Those are quite long paths, so I'm not going to try and figure out if there is something wrong with them ;) But you probably need to call updateValueAndValidity(). If it does not help, please create a plunker which demonstrates the issue. Commented Jul 29, 2017 at 19:10
  • I'll look at creating a plunker :) I was calling updateValueAndValidity() but it keeps crashing the browser saying maxmium call stack exceeded Commented Jul 29, 2017 at 19:17
  • Yeah, that would probably be because of this line: .subscribe(formData => this.checkFixturesFormValidity(formData)) checkFixturesFormValidity() is called everytime something happens in form values, and since you are using updateValueAndValidity() in that, "changes happen", which means subscribe is fired again and so it goes on, until browser crashes ;) You should consider a simple change event for the field instead. Commented Jul 29, 2017 at 19:25
  • 1
    Ah I see thanks. I'll just try that out first and if not I'll send a plunker. Thanks for your help so far :) Commented Jul 29, 2017 at 19:32
  • 1
    No problem. If there is no other errors, I think that updateValueAndValidity will solve your issue. As a sidenote, even if this is a matter of opinion, I have come to rely on events instead of using valueChanges unless I have some specific reason. With events you have tighter control on what is fired and when. When using valueChanges you cannot control it as tight, and performance wise this can become heavy. Even just on component initialization valueChanges will be fired x amount of times, which is usually totally unnecessary :) Commented Jul 29, 2017 at 19:48

1 Answer 1

3

I have somewhat been able to shorten all those long property paths, since in the iterations, we can pass for example the object you have named in your *ngFor, so that in methods we do not need to start with this.myFormFixtures.controls....

So what we have done here is to set a change event to your select. Since we know that when user makes a selection, if the value is else than null, we set Validators.required.

So content of tbody would look like this (shortened):

<tr *ngFor="let fixture of myFormFixtures.controls.fixtures.controls; 
            let c=index" [formGroupName]="c">
  <td><input type="text" formControlName="name" /></td>
  <td class="teamColumn">
    <div formArrayName="participants">
      <div *ngFor="let thisParticipant of fixture.controls.participants.controls; 
                   let i=index" [formGroupName]="i">
        <!-- We pass the current formgroup and value of dropdown, 
             which is null, 1 or 2 -->
        <select formControlName="id 
           (change)="doSomething(fixture.controls.participants, 
                     thisParticipant.controls.id.value)">
          <option [ngValue]=null>Please select</option>
          <option [ngValue]="1">Team Red</option>
          <option [ngValue]="2">Team Blue</option>
        </select>
      </div>
    </div>
  </td>
</tr>

Then the doSomething() would look like this:

// form array and chosen value
doSomething(formArr, val) {
  for (let x of formArr.controls) {
    x.controls.id.setValidators([Validators.required]);
    x.controls.id.updateValueAndValidity();
  }      
}

Your demo: https://plnkr.co/edit/hovobDwYUgH3C6fbe7eB?p=preview

PS, this doesn't take in consideration if user deselects a value.

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

2 Comments

Thanks for this, this is awesome! If I need to modify it should the user deselect both dropdowns again, what would be the best approach? Loop through the array in the doSomething method and if all values in the array are null then clear the validators else set the validators? Also in your opinion it's best to avoid "valueChanges"?
Well as I commented on your question, I explained why I prefer change events. Especially in this case you noticed that you ran into problems with change detection, subscribe and calling the method :) As to your other question, here's one possibility to solve that if both are unchecked, the validator will be removed: plnkr.co/edit/EFUMbA2oKgHn5aZUWfOT?p=preview

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.