0

I'm trying to implement FormArray into my project to generate dynamic FormControls.But I'm stuck at this error which I'm not able to understand.Whenever I create a getter method to get my array from FormGroup this error pops up.I'm going to explain the error with all the details....

Error in Console

enter image description here

When I click at the line pointed with RED ARROW IN ABOVE IMAGE it takes me to this following line of code and points at let data = await lastValueFrom(res);

 public async getScheduleData(): Promise<SchemaSchedule> {
let res = this.http.get<SchemaSchedule>(`${this.host}/api/demo-schedule`);
let data = await lastValueFrom(res);
return data;

}

When I click at the line pointed with BLUE ARROW IN ABOVE IMAGE it takes me to this following line of code and points at dayForm.get

 get mondayArray(): FormArray {
        return this.dayForm.get('mondayArray') as FormArray;
      }

Detailed Code Related to Above Error in TS file

async ngOnInit() {
    this.scheduleResult = await this.service.getScheduleData();
    this.setDay(
      this.scheduleResult.monday![0]
    );

  }
  async setDay(d1: Duration) {
    this.dayForm = new FormGroup({
      mondayArray: new FormArray([
        new FormGroup({
          startTime: new FormControl(d1.startTime),
          endTime: new FormControl(d1.endTime),
        }),

      ]),
    });
  }
  get mondayArray(): FormArray {
    return this.dayForm.get('mondayArray') as FormArray;
  }

When I click at the line pointed with YELLOW ARROW IN ABOVE IMAGE it takes me to this following line of code and points at index as i

<div
        class="loop_class"
        *ngFor="
         let item of mondayArray.controls;
         index as i
          "
          >

Detailed Code Related to Above Error in html file

<div class="formArray" formArrayName="mondayArray">
                          <div
                            class="loop_class"
                            *ngFor="
                              let item of mondayArray.controls;
                              index as i
                            "
                          >
                            <div class="dynamic_Forms" [formGroupName]="i">
                              <div class="from">
                                <select
                                  name="from"
                                  id="a"
                                  class="text-sm rounded-lg focus:border-primary border border-[#BCBCBC] py-[10px] px-4 focus:outline-none"
                                  formControlName="startTime"
                                  [ngClass]="monState ? '' : 'hidden'"
                                >
                                  <option
                                    [value]="timing.value"
                                    *ngFor="let timing of timingStart"
                                    selected="{{ timing.selected }}"
                                  >
                                    {{ timing.time }}
                                  </option>
                                </select>
                              </div>
                              <div class="divider">
                                <img src="/assets/icons/add.svg" alt="" />
                              </div>
                              <div class="to">
                                <select
                                  name="to"
                                  class="text-sm rounded-lg focus:border-primary border border-[#BCBCBC] py-[10px] px-4 focus:outline-none"
                                  formControlName="endTime"
                                >
                                  <option
                                    [value]="timing.value"
                                    *ngFor="let timing of timingEnd"
                                    selected="{{ timing.selected }}"
                                  >
                                    {{ timing.time }}
                                  </option>
                                </select>
                              </div>
                            </div>
                          </div>
                        </div>

2 Answers 2

1

As dayForm is only instantiated after the response from an asynchronous http call (this.http.get<SchemaSchedule>('${this.host}/api/demo-schedule') which triggers setDay()), this means that from the component's initalisation until the GET call completes, dayForm may be undefined. It therefore helpful to think of dayForm's actual type as FormGroup | undefined

This then leads us to understand that if the getter is trying to access the form control 'mondayArray' but dayForm is undefined, an error will be thrown as the 'mondayArray' key is missing.

There are a couple of ways we could protect for this, both are valid depending on the use case

  1. Modify the mondayArray getter to correctly reflect that mondayArray will either be a FormArray or undefined. This will mean that you will have to null check mondayArray whenever you want to access any information on it (as it could be undefined)
get mondayArray(): FormArray | undefined {
  if (!this.dayForm) return undefined;
  return this.dayForm.get('mondayArray') as FormArray
}
  1. Force the getter to always return an array to satisfy typing mondayArray as FormArray. This means during initialisation, any code which needs to access mondayArray will be fed an empty array ([]) instead.
get mondayArray(): FormArray {
  return (this.dayForm?.get('mondayArray') || []) as FormArray;
}

If there are still errors after making one of these changes, you could try simplifying by using observables instead of async/await

service.ts

public getScheduleData$(): Observable<SchemaSchedule> {
  return this.http.get<SchemaSchedule>(`${this.host}/api/demo-schedule`);
}

component.ts

ngOnInit() {
  this.service.getScheduleData$()
    .subscribe(
      scheduleData => {
       this.setDay(this.scheduleData.monday[0]);
      }
    )
}

setDay(d1: Duration) {
  this.dayForm =
    new FormGroup({
      mondayArray: new FormArray([
        new FormGroup({
          startTime: new FormControl(d1.startTime),
          endTime: new FormControl(d1.endTime),
        }),
      ]),
    });
}
Sign up to request clarification or add additional context in comments.

7 Comments

Unfortunately none of the above solutions worked for me. I tried all of them one by one. With the first solution it throws two more undefined errors at this.mondayArray.push and let item of mondayArray.controls and when I put ! or ? at this.mondayArray!.push and mondayArray!.controls, It throws an error ` can not read` for both push and controls... and also the same get error as well. And when I use Observable it throws can not read get erros pointing at .subscribe...
Here is a stackblitz demonstrating the concept stackblitz.com/edit/angular-6rltd5?file=src/app/schedule/…. Nothing majorly different except I have simplified the template to demonstrate and replaced the http.get() call with an observable with a delay of 1000ms. I also .push() another item into mondayArray after another delay of 1000ms. At no point does the getter throw an error, despite dayForm being undefined for some of the process. Have a look and let me know if you have further questions
I really appreciate your work. But first thing is I don't have a stream of data coming from backend, the data is just an admin schedule single page, which I just have to update using PUT. so I'm not allowed to use Observable I have to use Promise as I'm emitting or handling single value. And the second thing is I tried to produce code from your stack blitz example. But now the data isn't showing in the first place. Maybe my dayForm is undefined somehow and data is not loading because we have an *ngIf="dayForm" in html....And I'm seeing the same can not read get error...
Not a problem :) Regarding Observable vs Promise, it is highly recommended to use Observable even for single-value use cases. It may not seem beneficial now, but for more complex streams (e.g. mutliple parallel/sequential http requests), RxJs is far superior to the promise APIs, and these happen a lot in medium-large sized apps. If you are still using promises or async/await and it is not working, is there a chance that your endpoint is not being called at all? Check whether setDay is being called, and if not, whether the GET request is fired at all?
Thanks a lot bro for spending soo much time on this issue. But I have solve the issue. And the thing is that the array was empty at the backend. And when I emit some data using swagger than everything started working fine.Again Thank alot.
|
0

The issue in my case was that the array was empty at backend. And Angular was receiving nothing in GET from api that's why it was giving the error due to empty get. When I put some data using swagger at the backend the issue resolved.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.