Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular waiting for a method to finish or a variable to be initialized

I have a service used as a local data store to share data across the app. This service makes a few rest calls in the beginning to initialize some data. It look like this:

Injectable()
export class DataStoreService {
     leadList: LeadModel[]
     optyList: ContactModel[]

     constructor(public dataService:DataService){
         this.initializeData()
     }

     initializeData() {

         this.dataService.getObjectData('opportunities').subscribe(
             (data) =>  this.optyList = data
         )

         this.dataService.getObjectData('leads').subscribe(
             (data) =>  this.leadList = data
         )
     }
}

I have a component page where I do below:

ngOnInit() {
     for(let lead of this.dataStore.leadList){
             if(lead.accepted == false)
              this.newLeadsList.push(lead)
     }
}

It is very obvious that if initialize data fails to finish the leadList may be undefined and this ngOnInit for loop will crash as well. So, how do I wait in the ngOnInit until initializeData finishes?

like image 474
Vik Avatar asked Jan 25 '18 21:01

Vik


People also ask

How can I wait until my HTTP request finishes in angular?

Use . toPromise on your observable followed by async/await .

What is promise in angular with example?

Promises can be executed by calling the then() and catch() methods. The then() method takes two callback functions as parameters and is invoked when a promise is either resolved or rejected. The catch() method takes one callback function and is invoked when an error occurs.

Does ngoninit wait for a function to complete?

However, if you're using ngOnInit instead of the constructor to wait for a function to complete, you're basically doing the equivalent of this: It will run the async function, but it WILL NOT wait for it to complete. If you notice sometimes it completes and sometimes it doesn't, it really just depends on the timing of your function.

Does ngzone wait for an async function to complete?

It will run the async function, but it WILL NOT wait for it to complete. If you notice sometimes it completes and sometimes it doesn't, it really just depends on the timing of your function. Using the ideas from this post, you can basically run outside zone.js. NgZone does not include scheduleMacroTask, but zone.js is imported already into angular.

How to get last emitted value from observable in angular?

Sometimes in Angular, you gotta wait for multiple HTTP requests to complete before displaying a page. That's when you should turn to forkJoin. The forkJoin operator gives you the last emitted value from any number of Observable s. However, it only does that once they all complete.

When to use Async or await () function in JavaScript?

Use of async or await () function: This method can be used if the exact time required in setTimeout () cannot be specified. The async keyword is used to create an asynchronous function that returns a promise that is either rejected or resolved.


2 Answers

remove initializeData() from constructor and do something like this:

public async initializeData()
{
   this.optyList = await this.dataService.getObjectData('opportunities').toPromise();
   this.leadList = await this.dataService.getObjectData('leads').toPromise();
}


async ngOnInit() 
{
     await initializeData();

     for(let lead of this.dataStore.leadList){
         if(lead.accepted == false)
         this.newLeadsList.push(lead)
     }  
}  

I made code from head so it can have some bugs - may be async should be also before function ngOnInit()... check this.

Async-await-toPromise make that your asynchronous function behaves as synchronous function... and JS wait until your toPromise finish before execute another promise...

UPDATE

If I understand you right: you wanna call your service "DataStoreService.initializeData()" once and use it in future in other components (without calling initializeData again) - ? - if yes then you need Service singleton (which can also use inside above async-await technique)

like image 108
Kamil Kiełczewski Avatar answered Sep 27 '22 21:09

Kamil Kiełczewski


What you can utilize is observables, in this case a BehaviorSubject which you subscribe to and always emits if there is a subscriber. After making the initial request, just call next and all components that are subscribing will get the value... when it eventually arrives:

DataStoreService:

import {BehaviorSubject} from 'rxjs/BehaviorSubject';

// ...

leadList: LeadModel[]
leadList$ = new BehaviorSubject<LeadModel[]>([])

constructor(public dataService:DataService){
  this.initializeData()
}

initializeData() {
  this.dataService.getObjectData('leads')
    .subscribe((data) =>  {
      this.leadList = data;
      this.leadList$.next(this.leadList)
    })
}

Then in your components, subscribe to leadList$:

leadList = <LeadModel[]>[];

ngOnInit() {
  this.dataStore.leadList$.subscribe(data => {
    this.leadList = data;
    // do your magic here :)
  })
}

Then remember to unsubscribe when component is destroyed!

like image 26
AT82 Avatar answered Sep 27 '22 22:09

AT82