Angular 2 绑定到计算 getter 给出调试错误

Angular 2 bind to computed getter gives debug erros

我正在使用 Angular 2 和 lodash。

我有一个关系模型,我有一个 getter 这样的:

get relationsPerType() {
    return _(this.Relations)
        .groupBy(p => p.Type)
        .toPairs()
        .map(p => ({
            type: <MessageRelationType>p[0],
            relations: <Relation[]>p[1]
        }));
}

将 relationsPerType 的值绑定到 *ngFor 指令

编辑:这是我的绑定:

<div *ngFor="#relationGroup of Message.relationsPerType">
    ...
</div>

给我以下错误:

Expression 'Message.relationsPerType in MessagesDetailsComponent@60:8' has changed after it was checked. Previous

这似乎是完全正确的,因为实际上每次调用时都会计算它。

无论如何,拥有 "computed" 这样的变量,我无法想象 Angular 2 变化检测如何检测到 relationsPerType 实际上已经发生变化。 比如将 getter 标记为不可变的东西??

我想更好的方法是:

a) 从开始

开始就将计算出的 getter 值存储在 属性 中

b) 使父对象不可变以便 Angular 不跟踪属性的变化

有更好的方法吗?

您应该缓存该值并改为绑定到缓存的值,或者创建一个在数据更改时发出新事件的可观察对象。

如果绑定到 {{relationsPerType}},每次都会创建一个新集合 Angular 检查是否发生了更改,并且 Angular 将此视为更改,因为它有两个不同的实例,即使它们可能包含相同的数据。

calcRelationsPerType() {
    this relationsPerType = _(this.Relations)
        .groupBy(p => p.Type)
        .toPairs()
        .map(p => ({
            type: <MessageRelationType>p[0],
            relations: <Relation[]>p[1]
        }));
}

然后像这样绑定应该可以正常工作:

<div *ngFor="#relationGroup of Message.relationsPerType">
    ...
</div>

经过一番挖掘,我找到了一种使用 装饰器 的更好方法,而不是 a) 和 b) 必须提供的方法,如:

a) 我不想放弃 getter 函数提供的 "lazy" 计算并使所有内容成为 属性

b) 不可变是一个可行的解决方案,但不适用于我的情况

所以,来自 C# 和大量面向方面的编程(参见 PostSharp),我终于设法创建了一个 缓存 属性 getter 装饰器 函数每个对象只计算一次:

function cachedProperty(target: any, key: string, descriptor: PropertyDescriptor) {
    let originalGetter = descriptor.get;
    descriptor.get = function () {
        let cachSetted = this[`__cachedsetted${key}`];
        if (typeof cachSetted !== 'undefined') {
            return this[`__cached${key}`];
        }
        this[`__cachedsetted${key}`] = true;

        return this[`__cached${key}`] = originalGetter.call(this);
    }
}

之后,所有需要更改的是使用 @cachedProperty 装饰器装饰 getter,如下所示:

    @cachedProperty
    get relationsPerType() {
        return _(this.Relations)
            .groupBy(p => p.Type)
            .toPairs()
            .map(p => ({
                type: <MessageRelationType>p[0],
                relations: <Relation[]>p[1]
            })).value();
    }

使用这个装饰器,对象只会改变一次,所以 Angular 2 依赖注入不要抱怨。此外,我没有松开 "lazy" 评估,也没有添加更改架构的辅助属性。

如果缓存变得陈旧,他当然必须处理他想要使缓存无效的情况。那将需要删除

`__cachedsetted${key}`

属性 从此.