I do an async service call to get a list of elements.
public elementList: Array<Element>;
...
this.service.getElementList().subscribe( list => {
this.elementList = list;
this.createFormGroup(list);
}
);
...
When I receive the list of elements (I have a subscription the the service call) I build the Form Group based on the list of elements. In the template, I have an *ngIf to paint the only if the list is bigger than zero.
*ngIf="elementList?.length>0"
As the elementList is obtained asynchronously, this ngIf does not work. I have read that I can use ngIf with AsnycPipe. So in the template I will have:
*ngIf="elementList$ && elementList$.length>0"
where elementList$ is the Observable returned from the service call.
public elementList$: Observable<Array<Element>>;
elementList$ =this.service.getElementList()
I would like to know how can I create the FormGroup once I receive the elementList. First I need to create the FormGroup and then the template should call the *ngIf that will paint the elements.
There can be many different approaches to go about doing this. I'll just share two approaches at the moment and if you feel any of these approaches don't satisfy your use case, we can consider a different approach(provided that you give more information on what exactly you're trying to achieve here).
You have two properties on your Component:
form$: Observable<FormGroup>;
list: Array<Element>;
Once you have the API response, instead of subscribeing to it, you map it and generate a form while also assigning the value of the response to the list property that you've declared. Something like this:
this.form$ = this.service.getElements()
.pipe(
map((list: Array<Element>) => {
this.list = list;
return this.fb.group({
elementId: [list[0].id],
elementDescription: [list[0].description]
});
})
);
And then you use it in the template, somewhat like this:
<form
*ngIf="form$ | async as form"
[formGroup]="form">
<label for="">Element Id: </label>
<select formControlName="elementId">
<option
*ngFor="let element of list"
[value]="element.id">
{{ element.id }}
</option>
</select>
<br>
<label for="">Element description: </label>
<select formControlName="elementDescription">
<option
*ngFor="let element of list"
[value]="element.description">
{{ element.description }}
</option>
</select>
</form>
You might want to club both the list and the FormGroup together. So you can create a single property in your Component:
elementListWithForm$: Observable<{ list: Array<Element>, form: FormGroup }>;
You'd then assign a value like this:
this.elementListWithForm$ = this.service.getElements()
.pipe(
map((list: Array<Element>) => ({
form: this.fb.group({
elementId: [list[0].id],
elementDescription: [list[0].description]
}),
list,
}))
);
And then you can use it in the template like this:
<form
*ngIf="(elementListWithForm$ | async) as formWithList"
[formGroup]="formWithList.form">
<label for="">Element Id: </label>
<select formControlName="elementId">
<option
*ngFor="let element of formWithList.list"
[value]="element.id">
{{ element.id }}
</option>
</select>
<br>
<label for="">Element description: </label>
<select formControlName="elementDescription">
<option
*ngFor="let element of formWithList.list"
[value]="element.description">
{{ element.description }}
</option>
</select>
</form>
Here's a Working Code Sample on StackBlitz for your ref.
PS: This approach is heavily inspired by the one I used in an article that I wrote about increasing the performance of a deeply nested reactive form in Angular for AngularInDepth. You might want to check that out as well.
Hope this helps. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With