我使用的是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对象有四个属性。这些属性可以是字符串,数字,数组或对象。我想要的是通过某种方式识别对这一基础属性的任何更改,以便我可以将其传播给模型,并将其状态调整为"脏"。
答案 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')
});