I have a child component that looks like this:
@Component({
selector: 'app-child',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
{{text}}
`
})
export class ChildComponent {
@Input() text = '';
constructor(public host: ElementRef) { }
}
And a parent component that looks like this:
@Component({
selector: 'app-parent',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-content></ng-content>`
})
export class ParentComponent {
@ContentChild(ChildComponent) child: ChildComponent;
constructor(private cdr: ChangeDetectorRef) { }
ngAfterContentInit() {
this.child.text = 'hello';
this.child.host.nativeElement.addEventListener('click', () => {
this.child.text = 'from click';
this.cdr.detectChanges();
});
}
The first assign to the text
property is working fine, but when I click the button and try to change the text
property again nothing is happening.
That's confusing because from what I know:
1. The click event should trigger change detection and the text property is different so it should have been updated.
2. I explicitly called detectChanges()
and this should check also the children from what I know.
What am I missing?
The problem is related to this issue reported on GitHub. It occurs when:
OnPush
change detection strategy is used for the child componentThe explanation given by AngularInDepth.com:
The compiler doesn't have a way to generate necessary information for checking the bindings since it can't find these bindings in the template. OnPush is tightly bound to the input bindings. What's important is that Angular checks the second part of the binding (prop in the example below), not the first (i):
<child [i]="prop">
to determine whether the change detection should be run for the child component. And it does so when checking parent component. If you don't show the compiler what parent property should be used to update child input binding, it can't generate necessary information used when checking the parent. So inspecting @Input on child components isn't enough. That's the mechanism of change detection and I don't see any way it could be changed.
One workaround suggested by yurzui in the discussion is to call ChangeDetectorRef.markForCheck
in the child component after setting the text
property, as shown in this stackblitz. As a matter of fact, it works without calling ChangeDetectorRef.detectChanges
in the parent component.
export class ChildComponent {
private _text = '';
@Input()
get text() {
return this._text;
}
set text(val) {
if (this._text !== val) {
this.cdRef.markForCheck();
}
this._text = val;
}
constructor(public host: ElementRef, private cdRef: ChangeDetectorRef) { }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With