【问题标题】:Angular 2 bind to computed getter gives debug errosAngular 2 绑定到计算的 getter 会导致调试错误
【发布时间】:2016-08-12 17:21:26
【问题描述】:

我正在使用 Angular 2 和 lodash。

我有一个带关系的模型,我有一个像这样的吸气剂:

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

将relationshipPerType 的值绑定到*ngFor 指令

编辑:这是我的绑定:

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

给我以下错误:

表达式“MessagesDetailsComponent@60:8 中的 Message.relationsPerType” 检查后有变化。上一页

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

无论如何,拥有像这样的“计算”变量,我无法想象 Angular 2 更改检测如何检测到 RelationsPerType 实际发生了变化。 比如将 getter 标记为不可变?

我想更好的方法是:

a) 从一开始就将计算得到的 getter 值存储在属性中

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

有没有更好的方法来做到这一点?

【问题讨论】:

    标签: javascript typescript angular decorator lodash


    【解决方案1】:

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

    如果您绑定到{{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>
    

    【讨论】:

    • 嘿,君特!我认为您的第一个 sn-p 中有一个错字:this relationsPerType = return _(this.Relations) ;-)
    • 感谢君特。您所说的实际上是“a)从一开始就将计算得到的 getter 值存储在属性中”,在数据绑定之前调用 calcRelationsPerType() 需要付出更多努力。
    • 很难判断您在问题中提供的信息是否付出了很多努力。您需要找到一种方法来在结果取决于值更改时重新计算它。恕我直言,最容易理解的解决方案是我在回答中显示的内容。更高级的是使用 observables。您的装饰器方法也很有趣,但我认为使缓存无效与调用重新计算非常相似。
    • 君特,你说得对,让缓存失效。 Observables 也是一个不错的选择。
    【解决方案2】:

    经过一番挖掘,我发现使用 decorators 的方法比 a) 和 b) 提供的更好,因为:

    a) 我不想放弃 getter 函数提供的“惰性”计算并将所有内容都变成属性

    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 依赖注入不会抱怨。此外,我不会放弃“惰性”评估,也不会添加更改架构的辅助属性。

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

    `__cachedsetted${key}`
    

    来自此的属性。

    【讨论】:

      猜你喜欢
      • 2013-11-29
      • 1970-01-01
      • 2011-11-11
      • 2020-05-08
      • 1970-01-01
      • 1970-01-01
      • 2011-01-10
      • 2013-09-23
      • 2020-12-01
      相关资源
      最近更新 更多