0

I'm working on some app where there is an object that has some properties and when I make a http request new properties are added to that object.

    this.user = {
      id: 1,
      name: 'John'
    }

    this.http.get('https://api.chucknorris.io/jokes/random')
      .subscribe(joke => this.user.joke = joke);

The problem is that change detection isn't performed when making the request (this article states that it should be performed as long as I'm not modifying the change detection strategy).

Thus, ngOnChanges isn't called in the child component where I passed the user object and where I'm doing some complicated stuff when the value arrives from the server.

I attached a simplified stackblitz demo.

Note: I know that I can pass a copy of that object when the new data arrives, but I think it should be a better approach.

Mateut Alin
  • 1,173
  • 4
  • 17
  • 34
  • https://stackblitz.com/edit/angular-th9oze?file=src/app/app.component.ts – AT82 Jul 22 '19 at 08:12
  • @AJT_82 good answer. Maybe add a sentence explaining why the code is correct? – Keenan Diggs Jul 22 '19 at 08:18
  • @AJT_82 Please don't mark my question as duplicate until you tell me why it's so. I've gave you a reference to an article that states that the answer to the "duplicated" question is not correct, unless you change the change detection strategy to `OnPush`. If I misunderstood the article please let me know. Btw, I was also taking about the change detection strategy in my question, so I think this is another reason to do not mark the question as duplicate – Mateut Alin Jul 22 '19 at 09:07
  • Like the duplicate states: `If you modify the content of an object, Angular won't recognize.` This is just a fact. If you were to use a primitive value, the change detection would pick up on that, but here we are dealing with an object and angular won't pick up the change unless the reference changes. Same would be, if this was an array and you would modify one object in the array, angular would not pick up on that. – AT82 Jul 22 '19 at 10:28
  • @AJT_82 I read the question and I understand this. But [this](https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html#what-causes-change) article says that `whenever some asynchronous operation has been performed, our application state might have changed`. So there must be someone wrong (the guy that answered the stackoverflow question OR the guy that wrote the article). IMO, the guy that wrote the stackoverflow question is wrong, so I can't believe that `If you modify the content of an object, Angular won't recognize`. Please give me an official refecence. – Mateut Alin Jul 22 '19 at 11:51
  • I modified my code. As you can see, change detection is performed every request, but `ngOnChanges` is not called every time. – Mateut Alin Jul 22 '19 at 12:18
  • You are changing a **property** in the object, you are not changing the reference. That is the issue here. I made some modification to your stackblitz, just like in the link you shared, that user is changing the reference, thus angular can see the change. Look at this demo... we are changing the reference for the object with: `.subscribe(joke => this.user = joke);` THAT works, but when you modify a single property angular will not catch that. The modified stackblitz: https://stackblitz.com/edit/angular-2avwx4?file=src/app/app.component.ts – AT82 Jul 22 '19 at 12:33
  • So the difference is... if you assign the **whole** object a new value, change detection will pick that up. But if you change just one **property** the change will not be picked up by change detection. Hopefully I could make it clearer :) So this really has not anything to do with http-request. This is just how angular change detection works. – AT82 Jul 22 '19 at 12:37
  • I modified again my code. As you can see, if I add a property to the object and if I display that property directly form the object, it works. Conclusion: change detection is triggered every time, but `ngOnChanges` is called only if the references are different. So, I think I don't deserve the duplicate tag :) – Mateut Alin Jul 22 '19 at 12:51
  • Sorry, my stackblitz was wrong, this is the one changing the whole reference, where we see that `ngOnChanges` is called since we change the reference of the object: https://stackblitz.com/edit/angular-2avwx4?file=src%2Fapp%2Fchild%2Fchild.component.ts You hit the nail on the head yourself. `ngOnChanges` doesn't trigger unless the reference changes. That is EXACTLY what the duplicate says. question is why ngOnChanges is not fired, and the answer states that `If if you have a binding to a property of an object or item of an array, Angular will check the binding, but ngOnChanges won't be called` – AT82 Jul 22 '19 at 13:11
  • Yes, but the problem was that I thought that change detection has a one to one relationship with `ngOnChanges`, but is doesn't – Mateut Alin Jul 22 '19 at 13:16
  • I added a couple more duplicates that all say the same ;) – AT82 Jul 22 '19 at 13:17
  • Sorry, don't understand what you mean with: `I thought that change detection has a one to one relationship with ngOnChanges` – AT82 Jul 22 '19 at 13:18
  • I meant that change detection is performed anyway, independently from `ngOnChanges`. If it wasn't so, it wouldn't display the joke in the child component when doing `{{user?.joke?.value}}` [(here)](https://stackblitz.com/edit/angular-urwrgt?file=src%2Fapp%2Fchild%2Fchild.component.html) – Mateut Alin Jul 22 '19 at 14:27
  • Yes, change detection is performed. But your question is about why ngOnChanges is not called. – AT82 Jul 22 '19 at 18:46
  • Actually, the title refers to change detection and I misunderstood the things mentioned above. However, I should edit my question – Mateut Alin Jul 22 '19 at 20:31
  • I don't know if you think that this has anything to do with the http-request. It doesn't. See here we put a value for `user.joke` after a second, we can see that it's emitted to child, but `ngOnChanges` is not fired, since we just changed a property. Hopefully makes clearer. So this has nothing to do with http, just how angular change detection works: https://stackblitz.com/edit/angular-bkqeq6?file=src/app/child/child.component.html – AT82 Jul 23 '19 at 11:04

1 Answers1

-1

Angular change detection doesn't fire unless it detects a new reference.

If you overwrite existing user, with it self and the new prop, angular will detect a new reference and update.

 this.http.get('https://api.chucknorris.io/jokes/random')
      .subscribe(joke => this.user = {...this.user, joke};
cnps
  • 151
  • 10
  • why the downvote? – cnps Jul 22 '19 at 09:04
  • because I've already mentioned "Note: I know that I can pass a copy of that object when the new data arrives, but I think it should be a better approach". And even so, @AJT_82 already gave me this answer – Mateut Alin Jul 22 '19 at 09:09
  • @MTZ Okay, I honestly didn't notice that. I still think it's the best approach, unless you really need the same reference. Have you tried with changeDetectorRef? – cnps Jul 22 '19 at 09:18
  • Yes. I've tried. – Mateut Alin Jul 22 '19 at 09:19