检测Ember对象的状态变化

时间:2014-12-11 12:56:51

标签: ember.js ember-data

我使用的是Ember-Data,我的一个属性是字典数据结构。我希望将此字典的任何更新转换为将父模型设置为"脏"状态。

所以这是配置:

模型

export default DS.Model.extend({
    // standard types
    foo: DS.attr('string'),
    bar: DS.attr('number'),
    baz: DS.attr('boolean'),
    // dictionary (aka, flexible set of name value pairs)
    dictionary: DS.attr('object')
});

变换

export default DS.Transform.extend({
  deserialize: function(serialized) {
    return Ember.Object.create(serialized);
  },

  serialize: function(deserialized) {
    return deserialized;
  }  
});

这是有效的,让我们暂时假设"字典"属性定义为:

{
    one: { prop1: foo, prop2: bar, prop3: baz },
    two: 2,
    three: "howdy",
    many: [{},{},{}]
}

这意味着Ember对象有四个属性。这些属性可以是字符串,数字,数组或对象。我想要的是通过某种方式识别对这一基础属性的任何更改,以便我可以将其传播给模型,并将其状态调整为"脏"。

1 个答案:

答案 0 :(得分:4)

TL; DR - Working JS Bin example

为了实现这一目标,您必须执行以下操作:
1.将原始对象及其所有嵌套深度属性反序列化为Ember对象,以便它们可以是Observable
2.在原始对象引用的每次更改时,动态地为模型添加观察者,因为它可以更改其内容和方案。
3.在每个原始对象引用更改上删除这些动态观察器并分配新的 4.所有动态属性更改都将设置timestamp属性,以便控制器可以监听它

这是" Deep"我写的变换是为了完成(1):

// app/transforms/deep.js

export
default DS.Transform.extend({
  deserializeRecursively: function(toTraverse) {
    var hash;
    if (Ember.isArray(toTraverse)) {
      return Ember.A(toTraverse.map(function(item) {
        return this.deserializeRecursively(item);
      }, this));
    } else if (!Ember.$.isPlainObject(toTraverse)) {
      return toTraverse;
    } else {
      hash = this.generatePlainObject(Ember.keys(toTraverse), Ember.keys(toTraverse).map(function(key) {
        return this.deserializeRecursively(Ember.get(toTraverse, key));
      }, this));
      return Ember.Object.create(hash);
    }
  },
  deserialize: function(serialized) {
    return this.deserializeRecursively(serialized);
  },
  serialize: function(deserialized) {
    return deserialized;
  },
  generatePlainObject: function(keys, values) {
    var ret = {};
    keys.forEach(function(key, i) {
      ret[key] = values[i];
    });
    return ret;
  }
});

这是具有深度原始物体的模型的混合物,其实现了(2)& (3)& (4)

// app/mixins/dynamic-observable.js

export
default Ember.Mixin.create({
  propertiesToAnalyze: [],
  registerRecursively: function(toTraverse, path, propsToObserve) {
    if (Ember.isArray(toTraverse)) {
      propsToObserve.addObject(path + '.@each');
      if (toTraverse.length > 0) {
        this.registerRecursively(toTraverse[0], path + '.@each', propsToObserve);
      }
    } else if (!(toTraverse instanceof Ember.Object)) {
      propsToObserve.addObject(path);
    } else {
      Ember.keys(toTraverse).forEach(function(propertyName) {
        this.registerRecursively(Ember.get(toTraverse, propertyName), path + '.' + propertyName, propsToObserve);
      }, this);
    }
  },
  addDynamicObserver: function(propertyNameToAnalyze) {
    var propertyToAnalyze = this.get(propertyNameToAnalyze),
      propsToObserve = Ember.A([]),
      currentDynamicProps = this.get('currentDynamicProps'),
      propsToRemove = currentDynamicProps.filter(function(prop) {
        return new RegExp('^' + prop + '.').test(prop);
      });
    propsToRemove.forEach(function(prop) {
      Ember.removeObserver(prop, this, dynamicPropertiesObserver)
    }, this);
    currentDynamicProps.removeObjects(propsToRemove);
    this.registerRecursively(propertyToAnalyze, propertyNameToAnalyze, propsToObserve);
    propsToObserve.forEach(function(prop) {
      Ember.addObserver(this, prop, this, 'dynamicPropertiesObserver');
    }, this);
    currentDynamicProps.addObjects(propsToObserve);
  },
  dynamicPropertiesObserver: function(sender, key, value, rev) {
    this.set('dynamicPropertyTimestamp', new Date().getTime())
  },
  addDynamicObservers: function() {
    this.get('propertiesToAnalyze').forEach(this.addDynamicObserver, this);
  },
  init: function() {
    this._super();
    this.get('propertiesToAnalyze').forEach(function(prop) {
      Ember.addObserver(this, prop, this, Ember.run.bind(this, this.addDynamicObserver, prop));
    }, this);
  },
  dynamicPropertyTimestamp: null,
  currentDynamicProps: Ember.A([])
});

这就是你在模型上使用mixin的方法:

// app/models/some-object.js

import DynamicObservable from 'app/mixins/dynamic-observable';
export
default DS.Model.extend(DynamicObservable, {
  dictionary: DS.attr('deep'),
  propertiesToAnalyze: ['dictionary']
});

最后,这是一个数组控制器,其模型是some-object模型

的数组
export
default Ember.ArrayController.extend({
  message: '',
  observeDictionaries: function() {
    this.set('message', 'A dictionary has been changed. change time: ' + new Date().getTime());
  }.observes('@each.dynamicPropertyTimestamp')
});