1

Any idea what I am doing wrong?

I need to set it up where I have Events that contain a FormGroup and it has Dates as another Array of FormGroup.

I am getting this error "ERROR TypeError: Cannot read properties of null (reading 'controls')"

What am I doing wrong?

<div class="control-row">
 <div class="control-column">
  <div formArrayName="events">
   @for (event of events.controls;let i = $index; track $index) {
    <div [formGroupName]="i">
      <input hidden formControlName="eventId" >
      <input hidden formControlName="title" >
      <input formControlName="detail" />
    </div>
    @for (date of dates(i).controls;let j = $index; track $index) {
     <div formArrayName="dates">
      <div [formGroupName]="j">
       <input hidden formControlName="dateId" >
       <input formControlName="eventDate" >
       <input formControlName="startTime" >
      </div>
     </div>
    }
  </div>
 </div>
</div>

This is my data structure

{
  "id":5483,
  "name":"BLA BLA",
  "events":[
    {"eventId":2554,"title":1,"detail":"details follows EN",
      "dates":[
        {"dateId":2558,"eventDate":"2015-11-20","startTime":"2015-11-20"}
      ]
    },
    {"eventId":2555,"title":2,"detail":"BLA BLA",
      "dates":[
        {"dateId":2559,"eventDate":"2015-11-21","startTime":"2015-11-21"},
        {"dateId":2560,"eventDate":"2015-11-22","startTime":"2015-11-22"}
      ]
    }
  ]
}

this.myForm = this.fb.group({
  id: new FormControl(0),
  name: new FormControl(''),
  events: this.fb.array([
    this.createEventFormGroup()
  ])
});

createEventFormGroup(): FormGroup {
    return this.fb.group({
      id: new FormControl(0),
      detail: new FormControl('')
      dates: this.fb.array([
        this.createDateFormGroup()
      ]),
    });
}

createDateFormGroup(): FormGroup {
    return this.fb.group({
      dateId: [''],
      eventDate: [''],
      startTime: [''],
    });
}

get events(): FormArray {
    return this.myForm.get('events') as FormArray;
}

dates(eventIndex: number)
{
    return this.events.at(eventIndex).get('dates') as FormArray;
}

addEvent(): void {
    this.events.push(this.createEventFormGroup());
}

removeEvent(index: number): void {
    this.events.removeAt(index);
}

addDate(eventIndex: number): void {
    this.dates(eventIndex).push(this.createDateFormGroup());
}

removeDate(eventIndex: number, dateIndex: number): void {
    this.dates(eventIndex).removeAt(dateIndex);
}

Any idea what is causing the null exception that datesForArr returns?

0

1 Answer 1

1

Your HTML structure is incorrect. The dates FormArray should be in the FormGroup element under the events FormArray.

The overall form structure should be as below:

<form [formGroup]="myForm">

  <!-- Form controls for id and name -->

  <div class="control-row">
    <div class="control-column">
      <div formArrayName="events">
        @for (event of events.controls; let i = $index; track $index) {
        <div [formGroupName]="i">
          <input hidden formControlName="eventId" />
          <input hidden formControlName="title" />
          <input formControlName="detail" />

          <div formArrayName="dates">
            @for (date of dates(i).controls; let j = $index; track $index) {
            <div [formGroupName]="j">
              <input hidden formControlName="dateId" />
              <input formControlName="eventDate" />
              <input formControlName="startTime" />
            </div>
            }
          </div>

          <hr />
        </div>
        }
      </div>
    </div>
  </div>
</form>

And you are missing the title FormControl in the event FormGroup.

createEventFormGroup(): FormGroup {
  return this.fb.group({
    eventId: new FormControl(0),
    title: new FormControl(0),
    detail: new FormControl(''),
    dates: this.fb.array([this.createDateFormGroup()]),
  });
}

Bonus

Note that your current implementation will initialize one element for the FormGroup in the events FormArray. Same goes to in the first FormGroup element for the events FormArray, you are also initialize one FormGroup element in the dates FormArray.

If you are looking for patching value based on the provided data, you will face the issues such as only binding the first element for events and dates array, duplicates entries when pushing to the events and dates array.

For this case, you need to reset the events and dates FormArray first before patching and pushing the FormGroup instance.

resetForm() {
  this.events.clear();

  this.myForm.reset();
}

patchForm() {
  this.myForm.patchValue(this.data, { emitEvent: false });
  for (const event of this.data.events) {
    const eventFormGroup = this.createEventFormGroup();
    const datesFormArray = eventFormGroup.get('dates') as FormArray;

    // Reset 'dates' FormArray before patching
    datesFormArray.clear();
    eventFormGroup.patchValue(event, { emitEvent: false });

    for (const date of event.dates) {
      const dateFormGroup = this.createDateFormGroup();
      dateFormGroup.patchValue(date, { emitEvent: false });

      datesFormArray.push(dateFormGroup);
    }

    this.events.push(eventFormGroup);
  }
}

Demo @ StackBlitz.

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

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.