3
votes

I have two components, both of which are set to OnPush. The parent component sets accountLoading to true once getAccount() is called, then sets accountLoading to false once the call is completed. As expected, the console outputs:

this.accountLoading true

followed by:

this.accountLoading false

Yet the template doesn't update, and is stuck thinking that accountLoading is true. How do I get the template to update as expected when the value changes? I'd like to keep the change detection as OnPush.

Parent component:

TypeScript:

public accountLoading: boolean;
...

getAccount() {
  this.accountLoading = true;
    this.authStore
        .pipe(select(fromAuthStore.getAccountData))
        .subscribe(account => {
          if (account) {
            this.accountLoading = false;
          }
          console.log('this.accountLoading', this.accountLoading);
        });

  // Loading account if it hasn't yet been loaded
  this.authService.getAccount();
}

HTML:

<child-component
  [accountLoading]="accountLoading">
</child-component>

Child component:

TypeScript:

@Input() accountLoading: boolean;
...

HTML:

<p *ngIf="accountLoading">
  Loading...
</p>
3

3 Answers

5
votes

Try a behavior subject

public accountLoading$: BehaviorSubject<boolean>(false);
...

getAccount() {
  this.accountLoading$.next(true);
    this.authStore
        .pipe(select(fromAuthStore.getAccountData))
        .subscribe(account => {
          if (account) {
            this.accountLoading$.next(false);
          }
        });

  // Loading account if it hasn't yet been loaded
  this.authService.getAccount();
}

and use the async pipe in the template

<p *ngIf="accountLoading$ | async">
  Loading...
</p>

I have written a library to take care of a lot of this sort of state management for you, https://github.com/adriandavidbrand/ngx-rxcache. Have a read about it here https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

0
votes

If both of your components use the OnPush change detection strategy, the child template will not be updated when the parent is. Your child component will need to implement the OnChanges lifecycle hook and you can trigger the change detection there, when the value of the input changes.

0
votes

I would do it with standard observable pattern:

public accountLoading$: Observable<boolean>;

...

getAccount() {
   this.accountLoading$ = this.authStore
        .pipe(select(fromAuthStore.getAccountData), map(account => !!account));


  // Loading account if it hasn't yet been loaded
  this.authService.getAccount();
}

HTML:

<child-component
  [accountLoading]="accountLoading$ | async">
</child-component>