setInput() triggers ngOnChanges and mark component when it should not

This issue has been tracked since 2022-09-18.

Which @angular/* package(s) are the source of the bug?

core

Is this a regression?

No

Description

I thought ComponentRef.setInput() will:

  • set input
  • mark component when neccessary
  • make sure ngOnChanges is called when neccessary

However it is called when OnPush component is used and input doesn't change so i think it is not correct behavior.

image

msedge_YwrPI42REg.mp4

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/angular-ivy-yo1znz?file=src/app/hello.component.ts

Please provide the exception or error you saw

No response

Please provide the environment you discovered this bug in (run ng version)

No response

Anything else?

Of course we can check it manually, but only "component.instance" is available for the developers from "PUBLIC API" - and that's problem in case of setters without getters etc.

pkozlowski-opensource wrote this answer on 2022-09-21

Currently the setInput is not comparing prev / current values and this was conscious design decision. Something we might revisit as I've heard this need expressed elsewhere.

montella1507 wrote this answer on 2022-09-21

Hi @pkozlowski-opensource , thanks for your answer.

If i am correct, prev / current values are compared "SOMEWHERE" higher than on component level, right? In some kind of "component host container", there are stored previous inputs and ngOnChanges may be called (and component is marked) based on the result of that compare.

AFAIK we don't have "public" access to that place so we cannot do that (compare prev/current) with public api?

pkozlowski-opensource wrote this answer on 2022-09-21

ngOnChanges doesn't do prev / current comparison, this happens before input values are updated (and hence it is not implemented in the current version of the API). So it is not a question of the public API availability. Currently you would have to remember prev values and do the comparison on your side. As I've mentioned, this is sth we might consider doing inside setInput, although it is not clear what the design should be and if this is the right direction at all...

montella1507 wrote this answer on 2022-09-21

So i have to do something like

_previousName = undefined;

setName(newName: string) {
  if (newName !== this._previousName) {
   this._previousName = newName;
    this.setInput('name', newName);
  }
}

for every input (in the universal way - i can use reflectComponentType() api to get list of the inputs though)

pkozlowski-opensource wrote this answer on 2022-09-28

So this is a feature request I've heard before and we might need to do it for router anyway so marking it as a feature request (to be clear: it is about actually invoking setInput only if the value being set is different from the previously set value).

I'm not sure yet how this should be implemented and if this should end up in core or not. But it is a legitimate feature request that should be implementable within the current design.

More Details About Repo
Owner Name angular
Repo Name angular
Full Name angular/angular
Language TypeScript
Created Date 2014-09-18
Updated Date 2022-09-30
Star Count 84091
Watcher Count 3064
Fork Count 22233
Issue Count 1203

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date