0

I have a list of department, which will be fetched from an API link.

When my angular app loads (I means after ngOnInit(): void call), I want :

  1. I will fetch all department as async
  2. Then I will await for all department to load
  3. Finally, I will save 1st department's id in a variable

I am in confusion where I should put my await

(Note: my code is working, API response comes to me, I am only stuck in the await part)

Here is how far I tried:

app.component.ts:

import { Component, OnInit } from '@angular/core';
import { DepartmentModel } from '../../user/models/individual-department.model';

@Component({
  selector: 'app-requisition-report-one',
  templateUrl: './requisition-report-one.component.html',
  styleUrls: ['./requisition-report-one.component.css']
})
export class RequisitionReportOneComponent implements OnInit {

  displayIndividualDepartmentList: DepartmentModel[] = [];

  currentDisplayDepartment: number = null;


  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.initializeDepartmentList();
  }


  initializeDepartmentList() {
    this.userService.GetAllIndividualDepartment().subscribe(
      async (data: DepartmentModel[]) => {
        this.displayIndividualDepartmentList = data;
        this.currentDisplayDepartment = await this.displayIndividualDepartmentList[0].department_id;
      }
    );
  }

  onShowButtonClick() {
    console.log(this.currentDisplayDepartment);
  }

}

My vscode is giving me a warning like this

'await' has no effect on the type of this expression.ts(80007)

Besides, on show button click, I am getting null value.

I want to get the first department's id in this variable currentDisplayDepartment from the list displayIndividualDepartmentList after awaiting for the list to be loaded.

TIA

  • Try removing both `async` and `await`. You seem to provide a callback function, and thus it is likely going to be invoked when the data is ready. – tevemadar Apr 12 '21 at 12:28
  • Does this answer your question? [How can I \`await\` on an Rx Observable?](https://stackoverflow.com/questions/34190375/how-can-i-await-on-an-rx-observable) – nubinub Apr 13 '21 at 12:42
  • @nubinub yes I wanted something like that, thank you –  Apr 14 '21 at 05:21

2 Answers2

2

RxJS observables don't rely on async/await feature, but rather on a subscription/callback pattern. Your callback function will be executed everytimes the observable returned by GetAllIndividualDepartment method from UserService emits a new value, and therefore does not require any async prefix. You can access data in a synchronous way with no need to use the await keyword.

 initializeDepartmentList() {
    this.userService.GetAllIndividualDepartment().subscribe(
      (data: DepartmentModel[]) => {
        this.displayIndividualDepartmentList = data;
        this.currentDisplayDepartment = this.displayIndividualDepartmentList[0].department_id;
      }
    );
  }

A quick explanation of RxJS observables and how they are different from the Promise you are used to work with.

nubinub
  • 1,898
  • 2
  • 15
  • 23
1

You can convert your Observable to a promise as below and use the async/await syntax as opposed to .then() syntax.

The toPromise() method will handle the subscribe/unsubscribe for you and will only emit when the observable has completed. In the case of HTTP observables they typically only emit one value (primitive or otherwise) and then complete.

You can then use async/await syntax as shown or use .then().

Using toPromise() with HTTP observeables is a very compelling and clean syntax making asynchronous code look synchronous and easier to follow.

  async initializeDepartmentList(): Promise<void> {
    const data: DepartmentModel[] = await this.userService.GetAllIndividualDepartment().toPromise();
    this.displayIndividualDepartmentList = data;
    this.currentDisplayDepartment = this.displayIndividualDepartmentList[0].department_id;
  }

Call like this...

async ngOnInit(): Promise<void> {

    await this.initializeDepartmentList();
.
.
}

Or with a self invoking function like this.


ngOnInit()
{
(async () => {
  await this.initializeDepartmentList();
})()
}

Just to add that toPromise is being deprecated in RxJS 7 and will be removed in RxJS 8.

In its place you can use lastValueFrom

const data = await lastValueFrom(this.userService.GetAllIndividualDepartment());

As a side note, if you want to initialise your application upon startup you can use...

app.module.ts

providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: appInitializeService,
      multi: true
    },

Something like this. A promise returning void. The app effectively waits for your prerequisites to load before continuing.

export const appInitializeService = (): () => Promise<void> => (): Promise<void> => new Promise((resolve) => resolve());
Robin Webb
  • 1,355
  • 1
  • 8
  • 15
  • 1
    The question would be: why use `Observables` in the first place, if you want to rely on the `Promise` api? – nubinub Apr 13 '21 at 12:40