我们正在使用RectTransforms来处理表单中的数据。数据流通常如下:input -> changeset -> model -> server -> model -> changeset -> input
。用户更改一些输入,这些输入反映在变更集中并经过验证。当用户按下submit
时,变更集将被烘焙到模型中,然后保存。
这就是问题所在。如果服务器拒绝接受更改,那么我们最终会得到处于损坏状态的模型。为了解决这个问题,我们实施了自定义save
方法来模仿model.save
所做的事情。
基本思想是我们从变更集中获取更改并将其提交给服务器asi,如果我们只是保存模型。如果失败了,那么基本上没有任何反应。变更集保持脏,底层模型不变。但是,当保存成功时,我们使用从服务器返回的数据来更新基础实体,方法基本上与正常保存实体时商店所做的相同。
这适用于属性和关系。唯一的问题是当我们需要检查可能在保存之后发生变化的关系时,他们会懒得更新它们。
非常感谢任何帮助或想法。
/*
* Saves an object by saving changes taken from a changeset.
* Updates the target object if the operation succeeds.
* @param changeset the changeset to save
* @return {Promise} a promise that resolves to an object returned by the server
*/
saveChangeset(changeset) {
const record = changeset.get('_content'); // not entirely a public field
const id = record.get('id');
const constructor = record.get('constructor');
const modelName = constructor.modelName;
const snapshot = changesetToSnapshot(changeset);
const operation = record.get('isNew') ? 'createRecord' : 'updateRecord';
record._internalModel.adapterWillCommit();
return this.adapterFor(modelName)[operation](this, constructor, snapshot)
.then(res => {
// propagate the changes
const model = this.modelFor(modelName);
const serializer = this.serializerFor(modelName);
const payload = serializer.normalizeResponse(this, model, res, id, operation);
if (payload.included) {
this.push({data: payload.included});
}
this.didSaveRecord(record._internalModel, {data: payload.data});
changeset.rollback();
return res;
});
},
/**
* Creates s snapshot-like object based on a changeset.
*/
export default function changesetToSnapshot(changeset) {
const model = changeset.get('_content'); // not entirely a public field
const isNew = model.get('isNew');
const id = model.get('id');
const constructor = model.get('constructor');
const modelName = constructor.modelName;
const changes = {};
changeset.get('changes').forEach(change => {
changes[change.key] = null; // The value does not matter
});
const filterByName = fn => (name, meta) => { // eslint-disable-line func-style
if (name in changes) {
fn(name, meta);
}
};
const fieldFilter = isNew ? fn => fn : filterByName;
return { // emulate a snapshot
_isChangeset: true,
type: constructor,
record: model,
modelName,
id,
eachAttribute: fn => model.eachAttribute(fieldFilter(fn)),
eachRelationship: fn => model.eachRelationship(fieldFilter(fn)),
attr: key => changeset.get(key),
belongsTo(key, options) {
assert('Snapshot from changeset can only return the id of a belongsTo relationship, not a snapshot of it', options && options.id);
return get(changeset, key + '.id');
},
hasMany(key, options) {
assert('Snapshot from changeset can only return the ids of a hasMany relationship, not an array of snapshots', options && options.ids);
return changeset.get(key).map(e => e.get('id'));
},
};
}