Limits of OnPush change detection when mutating @Input objects in Angular
0 reputation · 25 Apr 2021, 22:59 UTC
0 reputation · 25 Apr 2021, 22:59 UTC
Goal: Ensure that a component using the OnPush change detection strategy reflects updates made to an @Input object without replacing the object's reference.
Constraint: OnPush only marks a component for check when the @Input reference changes or an internal event occurs; mutating properties or array elements in place does not trigger a view update, leaving the UI stale. The documentation notes this behavior but does not prescribe a universal solution, leaving developers to decide between immutable patterns, manual change detection triggers, or other work‑arounds.
What is the recommended approach to mutate nested @Input properties while retaining OnPush performance? Is there a supported Angular API to manually signal a change detection run for a specific OnPush component without bypassing its semantics? Does Angular provide a built‑in mechanism to deep‑watch @Input objects under OnPush?
34775 reputation · 26 Apr 2021, 09:48 UTC
The recommended way to have an OnPush child reflect mutations of an @Input object is to either create a new immutable reference (e.g., spread the object) or manually schedule change detection for that child using ChangeDetectorRef.markForCheck() (or detectChanges()) inside the child component.
OnPush only runs change detection when the @Input reference changes or an internal Angular event (click, promise, etc.) occurs. Mutating properties or array elements in place does not change the reference, so the view stays stale.
markForCheck() puts the component on the check list for the next run without bypassing OnPush semantics.detectChanges() runs detection immediately on the component and its descendants, also respecting OnPush.ngDoCheck with custom equality, or Observable inputs with the async pipe.ChangeDetectorRef into the OnPush child component.ngOnChanges) call this.cdRef.markForCheck();this.inputObj = {...this.inputObj};If the mutation occurs outside Angular’s zone (e.g., via a native setTimeout or third‑party library), you may need to wrap the markForCheck() call in NgZone.run(() => ...) or use Promise.resolve().then(() => this.cdRef.markForCheck());. Let me know whether the mutation is triggered inside an Angular event handler or outside the zone so I can confirm if the extra zone step is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.