Reputation: 11
Hello, I try to understand the change detection of angular.
I have a parent component my-editable
for inline text edit and a child component my-input
with a styled input element. I'm working with reactive forms which is piped from parent to child component.
Now if the output property status
from child component emit a new event, my parent component call its method dummy()
without any expression.
What me confused is that the change detection in parent obviously triggered!
If I remove the line (status)="dummy()"
from my-editable.component.html
the error message (<small class="error">{{ formControl.errors.msg }}</small>
) in template wouldn't be shown.
In my opinion, the change detection should be triggered by (status)="cdr.detectChanges()"
or something similar.
Can anyone explain it to me?
my-input.component.ts
@Component({
selector: 'my-input',
...
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyInput implements OnInit, OnDestroy {
...
@Input('control')
set control(control: AbstractControl) {
this.formControl = control as FormControl;
this.formGroup = control.parent;
}
@Output('status') statusOutput = new EventEmitter<string>();
set status(status: string) {
if (status) {
this.statusOutput.emit(status);
this.statusClass = status.toLowerCase();
this.cdr.detectChanges();
} else {
this.statusClass = false;
}
}
public statusClass: false | string;
public formGroup: FormGroup | FormArray;
public formControl: FormControl;
...
constructor(
...
private cdr: ChangeDetectorRef
) {...}
public ngOnInit(): void {
this.status = this.formControl.status;
this.updateOnStatusChange();
...
}
...
private updateOnStatusChange(): void {
this.formControl.statusChanges
.pipe(pairwise(), untilDestroyed(this))
.subscribe(([lastStatus, status]) => {
if (status !== lastStatus) {
this.status = status;
}
});
}
}
my-input.component.html
<i [ngClass]="statusClass" *ngIf="statusClass"></i>
<input [formControl]="formControl" [placeholder]="placeholder">
my-editable.component.ts
@Component({
selector: 'my-editable',
...
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyEditableComponent {
...
@Input('control')
set control(control: AbstractControl) {
this.formControl = control as FormControl;
}
...
public formControl: FormControl;
...
public dummy() {} // <<< MAGIC
...
}
my-editable.component.html
...
<ng-container [ngSwitch]="currentMode">
<ng-template [ngSwitchCase]="EditModes.READONLY">
<p>{{ formControl.value }}</p>
</ng-template>
<ng-template [ngSwitchCase]="EditModes.EDITABLE">
<div class="wrapper">
<p>{{ formControl.value }}</p>
<button (click)="mode = EditModes.EDIT">
<i class="edit"></i>
</button>
</div>
</ng-template>
<ng-template [ngSwitchCase]="EditModes.EDIT">
<my-input
(status)="dummy()" <!-- MAGIC -->
[control]="formControl"
...
></my-input>
</ng-template>
</ng-container>
<ng-container [ngSwitch]="formControl.invalid && formControl.touched">
<ng-template [ngSwitchCase]="true">
<small class="error">{{ formControl.errors.msg }}</small>
</ng-template>
<ng-template [ngSwitchCase]="false">
<small class="description">{{ description }}</small>
</ng-template>
</ng-container>
Upvotes: 0
Views: 423
Reputation: 11979
Just watched this awesome talk yesterday.
You should find your answer somewhere between 14:30 and 17:00.
TLDR: when you have OnPush
, change detection will trigger when @Input
data changes, but also when internal events occur.
Upvotes: 0