'Angular change detection not triggered for descendents with OnPush strategy
I'm using an angular directive on form components that when they're submitted, their form group and all their inputs are marked as touched. Here is the component:
import { Directive, HostListener, Self } from '@angular/core';
import { ControlContainer } from '@angular/forms';
@Directive({
selector: 'form[formGroup]',
})
export class MarkAllAsTouchedDirective {
@HostListener('submit')
onSubmit(): void {
this.container.control?.markAllAsTouched();
this.cdr.detectChanges();
}
constructor(
@Self() private container: ControlContainer,
private cdr: ChangeDetectorRef
) {}
}
Now in my application, I'm nesting a component with a change detection strategy of OnPush inside my form component. Almost like below:
<form
[formGroup]="someForm"
(ngSubmit)="onSubmit()"
>
<app-account-form
[someForm]="someForm"
></app-account-form>
.
.
.
</form>
And the app-account-form component which has OnPush strategy is as follows:
import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-account-form',
templateUrl: './account-form.component.html',
styleUrls: ['./account-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccountFormComponent {
@Input() someForm!: FormGroup;
}
My problem is that the app-account-form component does not detect changes made to the someForm form group even though I'm calling the detectChanges method inside the MarkAllAsTouchedDirective directive above.
What is the problem here? Am I misunderstanding the implementation of detectChanges and if it is applying to all the descendants?
Solution 1:[1]
ChangeDetectorReference.detectChanges() triggers CD on a component and its children by respecting the CD strategy of the children. As the AccountFormComponent has OnPush strategy, it would only be marked for changes if the someForm input changed. To determine if the input changed, Angular does a simple === comparison, it is not a deep object comparison. In your case, even though the touch property of the someForm object changed, the reference is still the same, so the AccountFormComponent will not be marked to be checked.
Here is the code in Angular that does the comparison, quoted from this great article about Change Detection.
To gain more insights about CD:
The Angular DevTools for Chrome provide a profiler that lists all change detection events, shows the source for each event and also allows you to see which component has been checked during each CD cycle. Just make sure to select the "flame graph" and tick the "Show only change detection" checkbox. The components marked blue have been checked.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

