Angular detectChanges not working on child Compone

2019-07-27 12:24发布

问题:

Edit: I just don't know why, but change detection stop at the first child in the hierarchy. If I manually invoke change detection one level deeper (in sch-job-detail), then the values are updated.

I've built a MatTable with expandable rows.
The "expandable" row part is as follow:

<!-- Hidden cell -->
<ng-container matColumnDef="expandedDetail">
    <td mat-cell *matCellDef="let jobModel" [attr.colspan]="displayedColumns.length">
        <div
            class="detail-cell"
            *ngIf="jobModel.isExpanded"
            [@detailExpand]
        >
            <sch-job-detail
                [jobModel]="jobModel"
                ...
            ></sch-job-detail>
        </div>
    </td>
</ng-container>

As you can see, the table owns an array of JobModel(s), and each row receives its own JobModel instance.
JobModel is a wrapper for a FormGroup, which is also a "transposition" of a simple interface object Job.

sch-job-detail has others children Components, for example:

<!-- Toolbar -->
<div class="col">
    <sch-job-row-toolbar
        [isNew]="jobModel.isNew"
        [isEdit]="jobModel.isEdit"
        [isError]="jobModel.isError"
        [isValid]="jobModel.isValid"
        ...
    ></sch-job-row-toolbar>
</div>

On the expandable row I have a button which lets the user enter a new Cron expression, and that Cron expression is then added to the FormGroup's FormControl.

Inside the TableComponent:

public addCronExpression(jobModel: JobModel): void {
    this.matDialog
        .open<CronDialogSmartComponent, any, string>(CronDialogSmartComponent)
        .afterClosed()
        .pipe(filter<string>(c => !!c))
        .subscribe(c => {
                jobModel.addCronExpression(c)
                this.changeDetector.detectChanges()
            }
        )
}

JobModel#addCronExpression:

public addCronExpression(cronExpression: string): void {
    const cronExpressions = this.formGroup.controls.cronExpressions
    cronExpressions.setValue([...cronExpressions.value, cronExpression])
}

As you can see, being that I do not change the JobModel instance, I run detectChanges to update the TableComponent and its children, sch-job-row-toolbar included I suppose!

The thing is sch-job-row-toolbar does not seem to recalculate its bindings (ngOnChanges doesn't run).

So those values:

[isNew]="jobModel.isNew"
[isEdit]="jobModel.isEdit"
[isError]="jobModel.isError"
[isValid]="jobModel.isValid"

are not changed.
We can take JobModel#isEdit as example:

get isEdit(): boolean {
    return this.formGroup.dirty || Job.isEdit(this.job.status)
}

I have no clue what's happening, but I know that if I do press a button or switch a tab eberywhere else, sch-job-row-toolbar receives the updated values.

All Components use onPush strategy.

Explanatory GIF:

And, interestingly, when I do the same thing from the first Tab, it works!

Tried using Default change detection strategy, but it's the same result.