Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular detectChanges not working on child Component

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: enter image description here

And, interestingly, when I do the same thing from the first Tab, it works! enter image description here

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

like image 570
LppEdd Avatar asked Jan 15 '19 15:01

LppEdd


1 Answers

Why don't you use @Input and @Output in order to detect changes from parent to child component? I would like to suggest you to use @Input (parent => child) and to use @Output (child=> parent) instead of detectChanges().

like image 128
Newsmartware Avatar answered Oct 03 '22 22:10

Newsmartware