2

Please note: I know there is APP_INITIALIZER but this seems to work only on the top-level module (app.module.ts) of the application.

I have as parent component which loads some data:

In admin-area.component.ts:

ngOnInit(): void {

  forkJoin([
    this._userService.getMe(),
    this._placeService.getAll()
    ])
    .pipe(finalize(() => this.afterInit()))
    .subscribe(
      ([user, businesses]) => {
        this._appState.user = user;
        this._appState.businesses = businesses;
        this.afterInit();
      }
    );
}

The problem is that there are child components which rely on this._appState.user and this._appState.businesses.

I cannot load this with APP_INITIALIZER because the above data is only loaded, if the user is logged in. The "logged-in" area is a lazy-loaded module on it own.

So, the question is rather simple:

How can I make sure my data is loaded before child components try to display themselves?

This:

providers: [
  AdminAreaState,
  { provide: APP_INITIALIZER, useFactory: adminAreaInit, deps: [AdminAreaState], multi: true}
]

Does not work. adminAreaInit() is not getting called. This only works for my top-level module app.module.ts. So, APP_INITIALIZER does not seem to work on any other module, is that corrent?

What are my options here?


I think I need to provide some more details.

Note, that I am already trying to use *ngIf:

<div *ngIf="!business?.servicePlan">
  Please Upgrade
</div>

but the problem is that if I navigate to this particular page and refresh the page, business is always undefined. business is a result of finding the right business in the Array of businesses using the businessId.

At the time the child component gets loaded, I load additional data. In this case some reviews (comments).

Now, loadPage() loads one page of my requrested data for a business. _appState is a service which is supposed to be loaded first. If I do this:

  private loadPage() {
    console.log(this._appState);
    console.log(this._appState.businesses);
    this._appState.businesses.forEach(
      (b) => {
        console.log(b);
      });
    setTimeout(() => this._appState.businesses.forEach(
      (b) => {
        console.log(b);
      }
    ), 100);

    const businessId = this._appState.businessId;
    this.business = this._appState.businesses.find(b => b.id === businessId);
  }

This is what I get:

enter image description here

As you can see this._appState has businesses but this._appState.businesses does print an empty array.

_appState.businesses is just an Observable:

ApplicationState service:

@Injectable()
export class ApplicationState {

  public businessIdChange;

  public businessesChange;

  private _user = new BehaviorSubject<UserModel>(null);

  private _businessId = new BehaviorSubject<number>(null);

  private _businesses = new BehaviorSubject<Array<BusinessModel>>([]);

  constructor() {
    this.businessIdChange = this._businessId.asObservable();
    this.businessesChange = this._businesses.asObservable();
  }

  set user(value: UserModel) {
    this._user.next(value);
  }

  get user(): UserModel {
    return this._user.getValue();
  }

  set businessId(businessId: number) {

    if (businessId === this.businessId) {
      return;
    }

    this._businessId.next(businessId);
  }

  get businessId() {
    return this._businessId.getValue();
  }

  set businesses(value) {
    this._businesses.next(value);
  }

  get businesses(): Array<BusinessModel> {
    return this._businesses.getValue();
  }

}

I have no idea why I see this and I thought pre-loading the data would make sense anyway. But maybe I have a different issue here?

7
  • 1
    Is there something to prevent to use *ngIf="_appstate_user" in the template where the child components are loaded ? Commented Aug 14, 2019 at 8:27
  • Or stackoverflow.com/questions/51191231/… Commented Aug 14, 2019 at 8:29
  • @SGalea Can this work with forkJoin ? Commented Aug 14, 2019 at 8:41
  • I would use the technique @edkeveked suggested or resolvers. Commented Aug 14, 2019 at 8:43
  • 1
    This application might need some refactoring however you can solve this issue quickly <div *ngIf="business"> <div *ngIf="!business?.servicePlan"> Please Upgrade </div> </div> Read about Smart/Dumb components and OnPush change detection it will make your code very elegant and easier to reason about. Commented Aug 14, 2019 at 9:12

2 Answers 2

2

Use Angular Resolvers on component level. Check this for more information.

Sign up to request clarification or add additional context in comments.

6 Comments

I just tried this. It only works in the sense of "the data gets loaded" but the child is still rendered before the data has completely arrived.
@displayname can you show me your resolver? Usually Resolvers return with resolve() an Observable which is resolved before the component is rendered so the data is available
Since my question is already quite huge I pasted it here: pastecode.xyz/view/227132a3 I am just returning the Observable from forkJoin.
@displayname do not return a Subscription. It has to be an Observable! Set the data in a pipe with tap or something. Like this: pastecode.xyz/view/ab506506
Ah, I guess I need to use exhaustMap like here: stackoverflow.com/a/50575875/826983
|
0

If the child component are displayed by routing, then using resolvers might be a good option. Here is an example of doing so in the doc

If the parent component is simply calling the child component by using the child component tag, then *ngIf will be a good option - something like this

<child-component *ngIf="condition"></child-component>

Last option, would be to use ngOnInit to initialize the data in the child component.

4 Comments

I updated my quesiton. *ngIf is already my weapon of choice but it breaks as I refresh the page. In this case, the data is not loaded and my condition is true. (the condition asks for not null)
Maybe you need to use this condition instead "business && !business?.servicePlan"
This was suggested in another comment. I tried it but this only works in 50% of the time.
I think you need to tune that condition to satisfy your need.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.