Chip
Chip

Reputation: 268

ngOnInit starts before APP_INITIALIZER is done

APP_INITIALIZER runs a series of nested promises (I've tried subscribes with no difference in the result).

The APP_INITIALIZER needs to get authenticated before it goes to retrieve data from the API server. It also needs to pull two tables from the API server (in sequence).

In api.service, http/get authorization happen in a promise. After the promise (then), I go to get data from the API service.

The issue is the component ngOnInit() - It attempts to get the variables before they exist.

I have tried the following code in the component, but all that does is call the initData() twice.

this.people = await this.apiService.initData();

api.service:

async initData(): Promise<Person[]> {
        this.userData$ = this.http.get('/.auth/me', {observe: 'response'});
        this.userData$.toPromise().then( res => {
          this.resBody = res.body;
          this.token = res.body[0].access_token;
          this.getLegalSub(this.legalsubdeptsURL)
            .toPromise().then(legalsubdepts => {
              this.legalsubdepts = legalsubdepts;
              this.getPeopleData(this.personURL)
              .toPromise().then(people => {
                this.people = people;
                return this.people;
              });
            });
        });
      }
      return this.people;
    }

app.module

export function initData(appInitService: APIService) {
  return (): Promise<any> => { 
    return appInitService.initData();
  }
}
...
providers: [
    APIService,
    { provide: APP_INITIALIZER, useFactory: initData, deps: [APIService], multi: true }
  ],

the component that runs before APP_INITIALIZER is done

ngOnInit() {
    this.people = this.apiService.people;
    this.userName = this.apiService.userName;
      console.log("username");
      console.log(this.userName);
  }

I need to get the authorization before I can get data from the API server. I then need the data from the API server before I process the component.

I eventually get data, but not in time for the component.

Upvotes: 5

Views: 10622

Answers (2)

Chip
Chip

Reputation: 268

The Answer:

in api.service - initData() needs to return a Promise. The code goes on too far.

initAuth(): Promise<any> {
    this.userData$ = this.http.get('/.auth/me', {observe: 'response'});
    return this.userData$.toPromise()
}

initLegalSub(): Promise<any[]> {
   this.initAuth().then( res => {
       this.resBody = res.body;
       this.legalSub$ =  this.getLegalSub(this.legalsubdeptsURL).toPromise();
   });
   return this.legalSub$;
}

initPeople(): Promise<Person[]> {
    this.initAuth().then( res => {
        this.resBody = res.body;
        this.people$ = this.getPeopleData(this.personURL).toPromise();
    });
    return this.people$;
}

You'll notice both initLegalSub() and initPeople() call initAuth() - to get the authentication BEFORE they attempt to get data.

THEN, both of these return Promise data - that is used in people.component.

constructor( private apiService: APIService ) { 
    this.legalSub$ = this.apiService.initLegalSub();
    this.people$ = this.apiService.initPeople();
  }

The initialization of the data happens in the app.module

providers: [
    APIService,
    { provide: APP_INITIALIZER, useFactory: initPeople, deps: [APIService], multi: true },
    { provide: APP_INITIALIZER, useFactory: initLegalSub, deps: [APIService], multi: true }],

I should put in more logic to determine what to do IF the promise fails, but for now, this allows multiple initializers to happen asynchronously.

Upvotes: 0

Sergey Mell
Sergey Mell

Reputation: 8040

APP_INITIALIZER is a callback is invoked before an app is initialized. All registered initializers can optionally return a Promise. All initializer functions that return Promises must be resolved before the application is bootstrapped.

In your case, you return a promise but it resolves almost immediately because you do not wait for the response to be finished. You should wait for your promises and you can do this by means of await directive

async initData(): Promise<Person[]> {
        this.userData$ = this.http.get('/.auth/me', {observe: 'response'});
        await this.userData$.toPromise().then(async res => {
          this.resBody = res.body;
          this.token = res.body[0].access_token;
          return await this.getLegalSub(this.legalsubdeptsURL)
            .toPromise().then(async legalsubdepts => {
              this.legalsubdepts = legalsubdepts;
              return await this.getPeopleData(this.personURL)
              .toPromise().then(people => {
                this.people = people;
                return this.people;
              });
            });
        });
      }
      return this.people;
    }

Upvotes: 7

Related Questions