Angular detectChanges 不适用于子组件 [英] Angular detectChanges not working on child Component

查看:29
本文介绍了Angular detectChanges 不适用于子组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是不知道为什么,但更改检测会在层次结构中的第一个孩子处停止.如果我手动调用更深一层的更改检测(在 sch-job-detail 中),则值会更新.

我构建了一个带有可扩展行的 MatTable.
可扩展"行部分如下:

<ng-container matColumnDef="expandedDetail"><td mat-cell *matCellDef="let jobModel" [attr.colspan]="displayedColumns.length">

</td></ng-容器>

如您所见,该表拥有一个 JobModel(s) 数组,并且每一行接收其自己的 JobModel 实例.
JobModel 是一个 FormGroup 的包装器,它也是一个简单的接口对象 Job 的转置".

sch-job-detail 有其他子组件,例如:

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

在可扩展行上,我有一个按钮,可让用户输入新的 Cron 表达式,然后将该 Cron 表达式添加到 FormGroupFormControl.

TableComponent 内部:

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

JobModel#addCronExpression:

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

如您所见,由于我没有更改 JobModel 实例,所以我运行 detectChanges 来更新 TableComponent 及其子项,sch-job-row-toolbar 我想包括在内!

事情是 sch-job-row-toolbar 似乎没有重新计算它的绑定(ngOnChanges 没有运行).

所以这些值:

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

没有改变.
我们可以以 JobModel#isEdit 为例:

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

我不知道发生了什么,但我知道如果我按下按钮或在其他地方切换选项卡,sch-job-row-toolbar 会收到更新的值.

所有组件都使用 onPush 策略.

说明性 GIF:

而且,有趣的是,当我从第一个选项卡做同样的事情时,它就起作用了!

尝试使用 Default 更改检测策略,但结果相同.

解决方案

为什么不使用 @Input@Output 来检测从父到子组件?我建议您使用 @Input (parent => child) 并使用 @Output (child=> parent) 而不是 detectChanges().

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.

解决方案

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().

这篇关于Angular detectChanges 不适用于子组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆