When my Angular component HTML renders. I will get lots of errors that look like this
ERROR TypeError: Cannot read properties of undefined (reading 'someProperty')
In my HTML page, I have lots of bindings that look like this
<h1>{{event.title.toUpperCase()}}</h1>
In most components I call a service to fetch data from my controller in the 'ngOnInit()' and I know that one solution to prevent this error before the data comes back from my controller is to make the property nullable like this
event?.title
but this doesn't seem like a practical solution, especially if I have dozens on the page.
What I have tried -
QUESTION - What's the best solution to handle this so I don't keep getting these errors until my page data loads?
Should I have some conditional like *ngIf="event" and hide the page until the data is received and show some loading indicator while waiting? Or is some way to jsut prevent these error from showing in the console?
EDIT - here is the code I use to fetch data from the controller and set it to 'event'
// component
event: any;
// ngOnInit()
this.eventService.getEvent(this.id).pipe(take(1)).subscribe(response => {
this.event = response;
}, error => {
console.log(error);
});
// service
getEvent(id: number): Observable <any> {
return this.http.get<any>(this.baseUrl + 'events/' + id);
}
Defining a boolean variable and checking if data has been loaded is the best approach:
// component
event: any;
loaded: boolean;
// ngOnInit()
this.eventService.getEvent(this.id).pipe(take(1)).subscribe(response => {
this.event = response;
this.loaded = true;
}, error => {
console.log(error);
});
In your HTML page, place your HTML tags inside a section or a div tag as follows:
<section *ngIf="loaded">
// your HTML tags here
</section>
In this way you do not keep getting undefined errors and bindings will happen only when data is really loaded. That's it!
I would wrap your affected html into something like
<ng-container *ngIf="event">
...
</ng-container>
When reading your description i'd also suggest to use the observable that is your http-call as an async-binding in your template instead of mapping it to a concrete object "event".
To provide you some more information on that however more code is required from your side. [EDIT] Your JS:
// class member
event$: Observable<any> = of(undefined);
// whereever
this.event$ = this.eventService.getEvent(this.id).pipe(take(1),
catchError(error => {
console.log(error);
//or return something else
return throwError(error);
}));
Binding in HTML:
<ng-container *ngIf="(event$ | async) as event">
...
</ng-container>
[/EDIT]