【问题标题】:How to update Ember computed property when dynamic sub-property changes动态子属性更改时如何更新 Ember 计算属性
【发布时间】:2014-04-06 10:00:50
【问题描述】:

我无法使用 Ember Data,因为我们的数据模型有点复杂(例如,并非所有东西都有 id),而且我不喜欢缓存和其他一些东西;但是,我确实喜欢它的模型具有状态标志(isNew、isDeleting),所以我正在尝试实现一些功能。

基本要点是,无论何时从服务器检索模型,该数据都存储在 _data 对象中。每当用户随后更改某物的值时,该值都存储在 _attributes 对象中。通过比较两者,我可以看到模型是否脏。属性是用 app.attr 方法定义的。

app.Person = app.Model.extend({
  "id": app.attr("number"),
  "name": app.attr("string"),
  "age": app.attr("number")
});

attr 函数与 ember data 的非常相似。

function getValue(record, key, options) {
    var attrs = get(record, "_attributes"),
        data = get(record, "_data");
    if ( attrs.hasOwnProperty(key) ) {
        return attrs[key];
    } else if ( data.hasOwnProperty(key) ) {
        return data[key];
    } else if ( typeof options.defaultValue === "function" ) {
        return options.defaultValue();
    }
    return options.defaultValue; // may be undefined
}

app.attr = function(type, options) {
    options = options || {};
    var meta = {
        "type": type,
        "isAttribute": true,
        "options": options
    };
    return function(key, value) {
        if ( arguments.length > 1 ) {
            if ( key === "id" ) {
                console.error("id is readonly on "+this.constructor.toString());
            } else {
                if ( get(this, "_data."+key) === value ) {
                    delete get(this, "_attributes")[key]; // not sure if this really works? seems to from my testing. set(this, "_attributes."+key, undefined) literally set undefined, which did not work
                } else {
                    set(this, "_attributes."+key, value);
                }
            }
        }
        return getValue(this, key, options);
    }.property("_data").meta(meta);
};

然后在我的模型上,我有某些标志,例如 isNew 和 isDirty。 isDirty 是一个计算属性是有意义的,但它需要在任何属性更改时进行更新。我想也许 .property("_attributes.*") 会这样做,但事实并非如此。我难住了。我尝试将其设置为 volatile.. 这可确保在明确要求时获得正确的值,但模板不会在基础属性更改时更新。

对于我上面的模型,执行 .property("id", "name", "age") 是合适的,但这些会因模型而异。我能够在模型的 init 中获取当前模型的属性,但我找不到动态更改它们的方法。

这是我的模型:

app.Model = Ember.Object.extend({
    "_attributes": null,
    "_data": null,

    "isDeleted": false,
    "isNew": false,
    "isSaving": false,

    "isDirty": function() {
        var attrs = get(this, "_attributes");
        for ( k in attrs ) {
            if ( attrs.hasOwnProperty(k) ) {
                return true;
            }
        }
        return false;
    }.property("_attributes").readOnly(), // NOTE: not quite right

    "init": function() {
        this._super();
        set(this, "_data", {});
        set(this, "_attributes", {});

        var attrs = [];
        this.constructor.eachAttribute(function(name, meta) {
            attrs.push(name);
        });
        // NOTE: Can I do anything to attach all the attrs to isDirty here?
    }
});

app.Model.reopenClass({
    "attributes": Ember.computed(function() {
        var map = Ember.Map.create();
        this.eachComputedProperty(function(name, meta) {
            if ( meta.isAttribute ) {
                meta.name = name;
                map.set(name, meta);
            }
        });
        return map;
    }),
    "eachAttribute": function(callback, binding) {
        get(this, "attributes").forEach(function(name, meta) {
            callback.apply(binding, arguments);
        }, binding);
    }
});

我用代码和测试创建了一个 jsFiddle:http://jsfiddle.net/2uCn3/3/

所以知道如何让 isDirty 在属性更改时进行更新吗? 同样(如在 jsFiddle 中所见),如果 _data.someProperty 直接更改,则 attr 仍将返回旧的默认值,因此我也对其进行了测试..但本质上是相同的问题。

我考虑过使用像 _attributesChanged 这样的计数器并使用 incrementProperty,但它看起来并不可靠而且看起来很糟糕。

我还考虑将 _attributes 和 _data 转换为数组,然后 @each 可能会有一些用处。

希望有更清洁的方法。我仍在试图弄清楚 ember 数据是如何做到的。

更新:我发现如果您直接操作 _data.property,Ember Data 也会出现错误,我想出了一个解决方法,但 GJK 的解决方案要好得多。

【问题讨论】:

    标签: javascript ember.js


    【解决方案1】:

    我也为 Ember 编写了自己的持久层,我解决它的方法是每次更改 _attributes 对象时调用 this.notifyPropertyChange('_attributes')。我看不到您的所有代码,但是如果您将编辑 _attributes 限制为仅几个 mutator 方法,那么您应该只需要在 3 或 4 个地方调用它。然后对于您的计算属性,您可以让它依赖于 _attributes 属性。

    在您的代码中,只需在 attr 函数内的两个位置调用它就足够了。 (假设这是修改_attributes内容的唯一方法。)

     if ( get(this, "_data."+key) === value ) {
        delete get(this, "_attributes")[key];
        this.notifyPropertyChange('_attributes');
     } else {
        set(this, "_attributes."+key, value);
        this.notifyPropertyChange('_attributes');
     }
    

    【讨论】:

      猜你喜欢
      • 2014-03-01
      • 1970-01-01
      • 2012-08-04
      • 2019-11-09
      • 2017-04-10
      • 1970-01-01
      • 2019-08-11
      • 1970-01-01
      • 2019-06-06
      相关资源
      最近更新 更多