Search code examples
angularasync-awaitpromisengoninit

Angular async ngOnInit instantiating a needed property


I have a property in an Angular component, instantiated through a service inside async ngOnInit:

txtItems: TextItems[];
 
constructor(private textItemService: TextItemService) {}

async ngOnInit() {    
    // await on promise
    this.txtItems = await this.textItemService.getAllAsync();
}

where getAllAsync returns a promise:

async getAllAsync(): Promise<TextItems[]> {
   return await this.http.get<TextItems[]>(this.url).toPromise();
}

ngOnInit runs asynchronously so that the page doesn't have to hang until items are loaded (which is a good thing), but sometimes this.txtItems is accessed from the template before it has finished loading, which results in an exception.

I can make ngOnInit not async, but then the page will load slowly.

Should I use txtItems: Promise<TextItems[]> or something like that, and then access it from everywhere using the async pipe? What is the correct approach when waiting for async services?


Solution

  • This is a classic case of converting an observable to a promise without any substantiated advantages. And given that the output from the HTTP request is used only in the template, you could return the observable from service and use it's value in the template using async pipe.

    Service

    getAllAsync(): Observable<TextItems[]> {
       return this.http.get<TextItems[]>(this.url);
    }
    

    Component

    public txtItems$: Observable<TextItems[]>;
    
    constructor(private textItemService: TextItemService) {
      this.txtItems$ = this.textItemService.getAllAsync();   // <-- no `await`, only assignment
    }
    

    Template

    <ng-container *ngIf="(txtItems$ | async) as txtItems">
      <!-- use `txtItems` -->
    </ng-container>