我有两个有亲子关系的模型:训练和锻炼:
App.Training = DS.Model.extend({
exercises: DS.hasMany('App.Exercise')
})
App.Exercise = DS.Model.extend({
training: DS.belongsTo('App.Training')
})
我希望有一个页面,其中显示了所有相关练习的训练。如果用户按下Edit
按钮,页面将变为可编辑状态,并可添加新练习。我还想要一个Cancel
按钮,它会丢弃所做的所有更改。
这是我的控制器:
App.TrainingsShowController = Em.ObjectController.extend({
editing: false,
edit: function() {
this.set('editing', true);
transaction = this.get('store').transaction();
transaction.add(this.get('model'));
this.get('model.exercises').forEach(function(x){
transaction.add(x);
});
},
cancel: function() {
this.set('editing', false);
this.get('model.transaction').rollback();
},
save: function() {
this.set('editing', false);
this.get('model.transaction').commit();
},
addExercise: function() {
this.get('model.exercises').createRecord({});
}
})
控制器中有四个事件处理程序:
edit
:用户按下Edit
按钮:创建了一个事务,页面进入“编辑”模式。cancel
:用户按下Cancel
按钮:事务回滚并返回“正常”模式。save
:用户按下了Save
按钮:提交了事务并返回“正常”模式。addExercise
:用户按下了Add exercise
按钮:创建了一个新练习(在同一个交易中)并添加到培训中。除了新创建的记录之外,回滚功能正常工作:如果我按下Edit
按钮,添加新练习并按下Cancel
按钮,新创建的练习将保留在页面上。
摆脱丢弃的儿童记录的最佳方法是什么?
更新
我创建了一个jsFiddle来重现问题,但它确实有效。与我的申请不同,我使用了DS.FixtureAdapter
:http://jsfiddle.net/tothda/LaXLG/13/
然后我使用DS.RESTAdapter
创建了另一个,问题出现了:http://jsfiddle.net/tothda/qwZc4/5/
在小提琴中尝试:编辑,添加新内容然后再回滚。
我想通了,在RESTAdapter的情况下,当我将新的子记录添加到hasMany
关系时,父记录不会变脏。这似乎很好,但当我回滚事务时,新创建的子记录保留在父ManyArray
中。
我仍然不知道,处理这种情况的最佳方法是什么。
答案 0 :(得分:12)
在Ember Data中非常缺乏hasMany和belongsTo关系的正确脏检查和回滚。它目前的行为方式通常被报告为错误。对于很多开发人员来说,这是一个很大的痛点,目前正在讨论如何解决这个问题:
https://github.com/emberjs/rfcs/pull/21
在有适当的解决方案之前,您可以使用以下方法解决此问题。
首先,您要重新打开DS.Model并对其进行扩展。如果你正在使用全局变量,你可以把它(例如DS.Model.reopen({}))放在任何地方,但是如果你使用Ember CLI,最好创建一个初始化器(例如,ember g初始化模型):
import DS from 'ember-data';
export function initialize(/* container, application */) {
DS.Model.reopen({
saveOriginalRelations: function() {
this.originalRelations = {};
this.constructor.eachRelationship(function(key, relationship) {
if (relationship.kind === 'belongsTo')
this.originalRelations[key] = this.get(key);
if (relationship.kind === 'hasMany')
this.originalRelations[key] = this.get(key).toArray();
}, this);
},
onLoad: function() {
this.saveOriginalRelations();
}.on('didLoad', 'didCreate', 'didUpdate'),
onReloading: function() {
if (!this.get('isReloading'))
this.saveOriginalRelations();
}.observes('isReloading'),
rollback: function() {
this._super();
if (!this.originalRelations)
return;
Ember.keys(this.originalRelations).forEach(function(key) {
// careful, as Ember.typeOf for ArrayProxy is 'instance'
if (Ember.isArray(this.get(key))) {
this.get(key).setObjects(this.originalRelations[key]);
this.get(key).filterBy('isDirty').invoke('rollback');
return;
}
if (Ember.typeOf(this.get(key)) === 'instance') {
this.set(key, this.originalRelations[key]);
return;
}
}, this);
},
isDeepDirty: function() {
if (this._super('isDirty'))
return true;
if (!this.originalRelations)
return false;
return Ember.keys(this.originalRelations).any(function(key) {
if (Ember.isArray(this.get(key))) {
if (this.get(key).anyBy('isDirty'))
return true;
if (this.get(key).get('length') !== this.originalRelations[key].length)
return true;
var dirty = false;
this.get(key).forEach(function(item, index) {
if (item.get('id') !== this.originalRelations[key][index].get('id'))
dirty = true;
}, this);
return dirty;
}
return this.get(key).get('isDirty') || this.get(key).get('id') !== this.originalRelations[key].get('id');
}, this);
}
});
};
export default {
name: 'model',
initialize: initialize
};
上面的代码实际上是在加载或更新时存储原始关系,以便以后可以用于回滚和脏检查。
model.rollback()现在应该回滚所有内容,包括hasMany和belongsTo关系。我们仍然没有完全解决“肮脏的”问题。虽然检查。为此,我们需要在模型的具体实现中覆盖isDirty。我们之所以需要在此处执行此操作并且我们无法在DS.Model中执行此操作,是因为DS.Model不知道要监视的属性更改。以下是使用Ember CLI的示例。除了你将这个类分配给像App.Book这样的类之外,同样的方法将与全局变量一起使用:
import DS from 'ember-data';
var Book = DS.Model.extend({
publisher: DS.belongsTo('publisher'),
authors: DS.hasMany('author'),
isDirty: function() {
return this.isDeepDirty();
}.property('currentState', 'publisher', 'authors.[]', 'authors.@each.isDirty').readOnly()
});
export default Book;
对于isDirty的依赖参数,请确保包含所有belongsTo关系,并且还包括' array。[]'和'数组。@ each.isDirty'对于每个hasMany关系。现在isDirty应该按预期工作。
答案 1 :(得分:1)
这不是很好但你可以通过手动弄脏父记录来强制它回滚:
parent.send('becomeDirty');
parent.rollback();
parent.get('children.length'); // => 0
答案 2 :(得分:0)
Ember Data : 1.0.0-beta.10+canary.7db210f29a
时,父级仍未设计为在回滚子项时使parentTraining.isDirty()
值为 true 。当一个属性发生变化时,Ember Data 认为父记录为dirty,而当DS.hasMany
数组发生变化时, <{3>}工作,所以你可以更新服务器上父项属性的任何更改。
对于上述案例,您要对新创建的子项执行rollback()
的方法是将.rollback()
替换为子记录上的.deleteRecord()
想要丢弃。然后,Ember Data会自动知道将它从DS.hasMany
数组中删除,然后您可以拍拍自己的背面以完成回滚!
答案 3 :(得分:0)
晚会,但我们走了:
我创建了一个解决此问题的插件。
只需致电rollbackRelationships()
,它就会回滚您的所有关系( belongsTo &amp; hasMany )。查看自述文件以获取更多选项。