Javascript Angular 2绑定到计算getter会导致调试错误

Javascript Angular 2绑定到计算getter会导致调试错误,javascript,typescript,angular,decorator,lodash,Javascript,Typescript,Angular,Decorator,Lodash,我正在与Angular 2和lodash合作 我有一个有关系的模型,我有一个这样的getter: get relationsPerType() { return _(this.Relations) .groupBy(p => p.Type) .toPairs() .map(p => ({ type: <MessageRelationType>p[0], relation

我正在与Angular 2和lodash合作

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

get relationsPerType() {
    return _(this.Relations)
        .groupBy(p => p.Type)
        .toPairs()
        .map(p => ({
            type: <MessageRelationType>p[0],
            relations: <Relation[]>p[1]
        }));
}
get relationsPerType(){
return(this.Relations)
.groupBy(p=>p.Type)
.toPairs()
.map(p=>({
类型:p[0],
关系:p[1]
}));
}
将relationsPerType的值绑定到*ngFor指令

编辑:这是我的绑定:


...
给我以下错误:

表达式“Message.relationsPerType in”MessagesDetailsComponent@60:8' 已在检查后更改。先前的

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

在任何情况下,有了这样的“计算”变量,我无法想象Angular 2 change detection如何检测到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]
        }));
}
calcreationspertype(){
this relationsPerType=389;(this.Relations)
.groupBy(p=>p.Type)
.toPairs()
.map(p=>({
类型:p[0],
关系:p[1]
}));
}
那么像这样的绑定应该可以很好地工作:

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

...

经过一番挖掘,我发现使用装饰器比a)和b)提供的更好,如下所示:

a) 我不想失去getter函数提供的“懒惰”计算,让一切都成为属性

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

因此,通过C#和大量面向方面的编程(参见PostSharp),我最终创建了一个缓存属性getter decorator函数,每个对象只计算一次:

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();
    }

来自此的属性。

嘿,甘特!我认为您的第一个代码片段中有一个输入错误:
this relationsPerType=return(this.Relations)
;-)谢谢你,甘特。您所说的实际上是“a)从一开始就将计算出的getter值存储在一个属性right中”,在数据绑定之前调用calcRelationsPerType()需要做很多额外的工作。很难说您在问题中提供的信息是否需要做很多工作。当结果所依赖的值发生变化时,您需要找到一种方法使其重新计算。最容易理解的解决方案是我在回答中所显示的内容。更先进的方法将是使用可观测数据。您的decorator方法也很有趣,但我认为使缓存无效与调用重新计算非常类似。Günter,关于使缓存无效,您是对的。在那里,可观测也是一个不错的选择。
    @cachedProperty
    get relationsPerType() {
        return _(this.Relations)
            .groupBy(p => p.Type)
            .toPairs()
            .map(p => ({
                type: <MessageRelationType>p[0],
                relations: <Relation[]>p[1]
            })).value();
    }
`__cachedsetted${key}`