How do I handle undefined properties from the html in my Angular application before I receive data from my controller?

2.3k views Asked by At

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 -

  1. Making them nullable with a '?' but that doesn't seem correct, especially if I always doing it
  2. Setting the object type being returning from the controller to a 'any' (ex. event: any), but in some cases it still shows an error

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);
}

2

There are 2 answers

8
Malte On

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]

0
Omid Karami On

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!