强制 Angular 不对某些 @Input 进行更改检测

Force Angular to not do change detection on certain @Input

我有一个 Angular 组件,它在检测到变化时进行一些相当繁重的计算。

@Component (
    selector: 'my-table',
    ... 400+ lines of angular template ...
)
class MyTable implements OnDestroy, AfterContentInit, OnChanges {
    ...
    @override
    ngOnChanges(Map<String, SimpleChange> changes) {
        log.info("ngOnChanges" + changes.keys.toString());
        _buildTableContent();
    }
    ...
}

当所有输入为 Stringintbool 时,这会非常有效;换句话说,ngOnChanges 只有在这些属性实际发生变化时才会触发。

我现在需要为其中一个字段添加自定义呈现器,以便呈现不仅仅是简单 String 的数据,我使用

@Input("customRenderer") Function customRenderer;

customRenderer 函数现在将决定它是否应该 return 原样的值,或者如果该值是一个对象/列表,则从中提取某些值并 return可读字符串,而不仅仅是 Instance of ___;

只要我添加这个 @Input("customRenderer"),即使那个函数引用没有改变,ngOnChanges 也会一直触发。

有什么方法可以让 Angular 在设置初始值后不触发某些字段的更改检测?

一个快速的破解方法是在 ngOnChanges 函数中使用一个 if 语句来检查 customRenderer 是否是唯一的变化,但变化检测将继续触发,这感觉效率低下。

Angular 是否有一个我可以覆盖的钩子,基本上会说,如果字段是 customRenderer,不触发变化检测,否则进行正常的变化检测?

根据@pankaj-parkar 的回答更新:

@Component (
    selector: 'my-table',
    ... 400+ lines of angular template ...
)
class MyTable implements OnDestroy, AfterContentInit, OnChanges, DoCheck {

    ...

    final ChangeDetectorRef cdr;

    int renderOldValue = 0;
    @Input() int render = 0;

    MyTable(this.cdr);

    @override
    ngOnChanges(Map<String, SimpleChange> changes) {
        log.info("ngOnChanges" + changes.keys.toString());
        _buildTableContent();
    }

    @override
    ngDoCheck() {
        if (renderOldValue != render) {
            cdr.reattach();
            cdr.detectChanges();
            cdr.detach();
            renderOldValue = render;
        }
    }

    @override
    ngAfterContentInit() {

        // detach table from angular change detection;
        cdr.detach();

       ...

    }

    ...

}

现在的思路是调用render++手动触发变化检测

    <my-table 
        (change)="change($event)"
        (click2)="editResource($event)"
        [custom]="['tags', 'attributes']"
        [customRenderer]="customRenderer"
        [data]="data ?? []"
        [debug]="true"
        [editable]="enableQuickEdit ?? false"
        [loading]="loading ?? true"
        [render]="render ?? 0"
        [rowDropdownItems]="rowDropdownItems"
        [tableDropdownItems]="tableDropdownItems ?? []">
        <column *ngFor="let column of visibleColumns ?? []"
            [editable]="column.editable"
            [field]="column.field"
            [title]="column.title">
        </column>
    </my-table>

不过没什么区别...

对于这种情况,您应该考虑 detach 来自变更检测器树的组件(您可以在 ngOnInit 挂钩内完成或在组件元数据中使用 ChangeDetectionStrategy.Detach )并添加添加 ngDoCheck 为每张 CD 触发的生命周期挂钩。

MyTable(this.cd);

ngOnInit() {
   cd.detach();
}

在那个钩子中,你将专门检查你的条件所以只要条件得到满足,就连接你的变化检测器并再次分离它。因此,下一个变更检测周期将负责更新您的组件绑定并调用 ngOnChanges 方法。

ngDoCheck() {
    if(model.value == 'specific value') {
       cd.reattach();
       cd.detectChanges(); //run cd once again for enabled component
       cd.detach(); //detached component
       //or
       //cd.markForCheck();
    }
}

目前已找到可行的解决方法。 通过将这些函数包装在地图中,变化检测似乎可以正常运行:

final Map renderers = {
    "tags": (List<TagVO> tags) {
        final List<String> tagStrings = [];
        tags.forEach((tag) => tagStrings.add(tag.name));
        return tagStrings.join(", ");
    },
    "attributes": (List<Attribute> attributes) {
        return "ATTRIBUTES!!!";
    }
};

出于某种原因将它传递进来:

<my-table 
    ...
    [render]="render ?? 0"
    [renderers]="renderers"
    ...
    <column *ngFor="let column of visibleColumns ?? []"
        [editable]="column.editable"
        [field]="column.field"
        [title]="column.title">
    </column>
</my-table>

和@Input:

@Input("renderers") Map<String, Function> renderers;

将看看我是否可以在某个时候在独立项目中重现该问题并为其记录错误。