angulartypescriptngonchanges

ngOnChanges child method is not triggering when attempting to pass a new object


I have a parent page that passes user data to a child component like so:

<ng-container *ngIf="leaderboard">
    <app-leaderboard-preview [user]="user" (click)="goToLeaderboard()"></app-leaderboard-preview>
</ng-container>

The parent page ngOnInit() subscribes to an observable that returns a user object and sets the user variable (i removed other irrelevant queries from the combineLatest):

combineLatest([this.userQuery$]).subscribe((results) => {
  Promise.all([this.parseUser(results[4])])
})

It is my understanding that ngOnChanges() will not trigger unless a new object is created, so I assign the new user object as a new object to pass to the app-leaderboard-preview component using Object.assign()

  parseUser(user) {
    return new Promise((resolve) => {
      if(user) {
        this.user = Object.assign({}, user);
        resolve(user);
      } else {
        resolve(user);
      }
    })
  }

This loads the component, just fine, but the ranking of the user can change, so when a user swipes down to refresh the page, the value should be updated, but the component does not update. I use the following code (almost carbon copy as the above) to refresh the page (without a hard reload).

  doRefresh(event) {
    if (this.user) {
      //user
      this.userQuery$ = this.db.query$(`user/find?id=${this.user.id}`);
      combineLatest([this.userQuery$]).subscribe((results) => {
        Promise.all([this.parseUser(results[4])])
      })
  }

Which then runs the parseUser method to update the user object that the app-leaderboard-preview uses.

So this should trigger the ngOnChanges because I am passing a "new" object to the component. What am I missing?


Solution

  • Edit

    After some back and forth Jordan figured out he had an async pipe assigning to the user variable which was overriding his changes to this.user.

    *ngIf = user$ | async as user
    

    Original Answer

    Object.assign({}, object) should work. Tested on Angular 7.

    this.user = Object.assign({}, user);
    

    .. or if you are using lodash you can do.

    this.user = _.clone(user);
    this.user = _.cloneDeep(user);
    

    https://lodash.com/docs/4.17.11#clone

    https://lodash.com/docs/4.17.11#cloneDeep

    Stackblitz Demo: https://stackblitz.com/edit/ngonchangeswithobject