0

My angular 11 change detection only fires when i scroll down for initial page load. My observable emits correctly if i debug in the subscribe. any ideas?

my component:

public extras$: Observable<Order[]>;
  constructor(private userService: userService) {

  }
  async ngOnInit() {
    await this.load();
  }

  private async load() {
    this.userService.user$.subscribe(async user=> {
      if (user) {
        this.extras$ = of(await this.userService.getExtras(22456));
      }
    });
  }

my html component:

<div class="u-extras">
  <div class="main-heading"><span>User Extras</span></div>
  <ng-container *ngIf="extras$ | async as extras;else loader">
    <user-extra*ngFor="let extraof extras" [extra]=extra></user-extra>
  </ng-container>
</div>

<ng-template #loader>
  <loader-dots></loader-dots>
</ng-template>
  • The title is totally misunderstanding. The `Change Detection` is triggered automatically by angular when an input/output changes. You can decide which component gets updated automatically and which not by changing it in the `decorator` of the component. Also, reasons why an async `ngOnInit` is not a good practice: https://stackoverflow.com/questions/56092083/async-await-in-angular-ngoninit – Jacopo Sciampi Jun 17 '21 at 13:13
  • Can you please add show us the `decorator` of the component where you are getting the `user$` ? – Jacopo Sciampi Jun 17 '21 at 13:14

1 Answers1

1

This not is not the right way to create and use an Observable. You also do not need to use async. There is also no ngOnInit in a service.

I also prefer to declare a Subject as a private variable and expose a public Observable. This allows me to control who can emit values. It's important to note that I'm using a BehaviorSubject here instead of a plain Subject. Since your service will be set up prior to the component being initialized, we need to make sure that new subscribers (in this case your component) will be able to get the value. Regular Subjects won't emit a value to new subscribers, where BehaviorSubjects will.

//User a behavior subject, which will emit values to new subscribers
private _extras = new BehaviorSubject<Order[]>([]);
private userSub: Subscription;

constructor(private userService: userService) {
    load();
}

get extras$(): Observable<Order[]>{
    return this._extras.asObservable();
}

//Services do not have ngOnInit(), but they do have ngOnDestroy
//ngOnInit(){
//    load();
//}

ngOnDestroy(){
    //be sure to clean up any subscriptions
    this.userSub.unsubscribe();
}

private load(){
    //listen for the next user to be emitted
    this.userSub = this.userService.user$.subscribe(user => {
        //when a new user is emitted, go get some extra data
        this.userService.getExtras(22456).subscribe(extras => {
            //emit the extra data
            this.extras$.next(extras);
        })
    })
}

Here is a stackblitz that demonstrates

spots
  • 2,483
  • 5
  • 23
  • 38
  • The same thing happens, if i manually invoke change detection it appears but obviously thats not ideal – Stuart Lexington Jun 17 '21 at 13:06
  • You are right! Change detection is not occurring with my original example. The reason being that a `Subject` won't emit its value to new subscribers. I've edited my answer and provided a working stackblitz. – spots Jun 17 '21 at 13:37
  • "There is also no ngOnInit in a service.": this is a component it seems, not a service – manuch100 Feb 20 '23 at 15:04