Reputation: 60
We have 2 arrays Speisekarte and Essensplan - I want to call the method printName, to print out the name, I get from the ID.
The problem is, "name" is always undefined.
"ERROR TypeError: Cannot read property 'name' of undefined"
How can i fix async in methods in templates?
Template (Async in printName(essenName) )
<div *ngFor="let i of essensplan"> <br />
<div><b>Woche : {{i.id}}</b></div> <br />
<button *ngFor="let id of i.essenProWoche" (click)="print(id)">Gericht
** {{printName(id)}} ** </button>
Component
ngOnInit() {
console.log("ngOnInite essensplan.component")
this.getSpeisekarte();
this.getEssensplan();
}
printName(id: number) {
this.essenName = this.speisekarte.find(i => i.id == id ).name
getSpeisekarte(): void {
this.essenService.getSpeisekarte()
.subscribe(speisekarte => this.speisekarte = speisekarte);
}
getEssensplan(): void {
this.essensplanService.getEssensplan()
.subscribe(essensplan => this.essensplan = essensplan)
}
Upvotes: 0
Views: 59
Reputation: 6488
Use Observables for your problem. They will significantly reduce and improve your code. Angular has a buildin pipe called async-pipe which subscribes as long as the component is alive.
export class MyEssenComponent {
essensPlan$: Observable<Essensplan>;
speisekarte$: Observable<Speisekarte>;
ngOnInit(): void {
this.essensPlan$ = this.essensplanService.getEssensplan();
this.speisekarte$ = this.essenService.getSpeisekarte();
}
get name(id: string): Observable<string> {
return this.speisekarte$.pipe(
filter(speisekarte => speisekarte.id === id),
map(speisekarten => speisekarten[0] ? speisekarten[0].name : '')
);
}
}
Then you can easily rewrite your template to
<div *ngFor="let i of essensplan$ | async"> <br />
<div><b>Woche : {{i.id}}</b></div> <br />
<button *ngFor="let id of i.essenProWoche" (click)="print(id)">
Gericht ** {{name(id) | async}} **
</button>
</div>
Also I recommend to choose more meaningful variable names as the are totally irritating. E.g. you chose essenProWoche attribute for an array of IDs, or just i as variable.
Upvotes: 0
Reputation: 371
You can use AsyncPipe "The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks."
<div *ngFor="let i of essensplan | async "> <br />
or use NgIF operator to show safely the view.
Please check the Answer for the same problem.
Upvotes: 0
Reputation: 6844
I'm not sure if it is possible to delay the entire component until both HTTP calls have completed. Instead of that, you can modify printName
to handle the case where this.speisekarte
hasn't been loaded yet. Then, tell the component to reload once it has.
Component
constructor(private changeDetector: ChangeDetectorRef) {}
ngOnInit() {
console.log("ngOnInite essensplan.component")
this.getSpeisekarte();
this.getEssensplan();
}
printName(id: number) {
const match = this.speisekarte.find(i => i.id == id );
return match ? match.name : "";
}
getSpeisekarte(): void {
this.essenService.getSpeisekarte()
.subscribe(speisekarte => {
this.speisekarte = speisekarte;
this.changeDetector.markForCheck();
});
}
getEssensplan(): void {
this.essensplanService.getEssensplan()
.subscribe(essensplan => {
this.essensplan = essensplan;
this.changeDetector.markForCheck();
});
}
Now, regardless of the order the HTTP requests complete, the templates will not error (although the names will be blank if getEssensplan
finishes before getSpeisekarte
Note: I changed printName
to return a value, since that will be necessary to display it
Upvotes: 1