如何取消/恢复对可观察模型的更改(或用未修改的副本替换数组中的模型)
How to cancel/revert changes to an observable model (or replace model in array with untouched copy)
我有一个viewModel与一个observableArray对象与可观察变量。
我的模板显示了带有编辑按钮的数据,该按钮隐藏了显示元素,并显示了绑定值的输入元素。您可以开始编辑数据,然后可以选择取消。我希望这个取消将恢复到对象的未更改版本。
我已经尝试过克隆对象,通过这样做:
viewModel.tempContact = jQuery.extend({}, contact);
或
viewModel.tempContact = jQuery.extend(true, {}, contact);
但视图模型。当contact被修改时,tempContact也会被修改。
是否有任何内置到KnockoutJS来处理这种情况,或者我最好只是创建一个具有完全相同的详细信息的新联系人,并将修改后的联系人替换为取消上的新联系人?
任何建议都非常感谢。谢谢!
有几种方法可以处理这样的事情。您可以使用与当前对象相同的值构造一个新对象,并在取消时将其丢弃。你可以添加额外的可观察对象来绑定到编辑字段,并将它们持久化到accept上,或者看看这篇文章,了解如何将这个功能封装到一个可重用的类型中(这是我的首选方法)。
我在寻找解决类似问题的方法时偶然发现了这篇文章,我想我应该把我的方法和解决方案发布给下一个人。
我同意你的想法——克隆对象,然后用"undo"键重新填充旧数据:
1)复制数据对象到一个新的页面变量("_initData")2)从原始服务器对象创建Observable3) on "undo"重载observable with unchanged data ("_initData")
简化JS:var _viewModel;var _initData = {};
$(function () {
//on initial load
$.post("/loadMeUp", {}, function (data) {
$.extend(_initData , data);
_viewModel = ko.mapping.fromJS(data);
});
//to rollback changes
$("#undo").live("click", function (){
var data = {};
$.extend(data, _initData );
ko.mapping.fromJS(data, {}, _viewModel);
});
//when updating whole object from server
$("#updateFromServer).live("click", function(){
$.post("/loadMeUp", {}, function (data) {
$.extend(_initData , data);
ko.mapping.fromJS(data, {}, _viewModel);
});
});
//to just load a single item within the observable (for instance, nested objects)
$("#updateSpecificItemFromServer).live("click", function(){
$.post("/loadMeUpSpecificItem", {}, function (data) {
$.extend(_initData.SpecificItem, data);
ko.mapping.fromJS(data, {}, _viewModel.SpecificItem);
});
});
//updating subItems from both lists
$(".removeSpecificItem").live("click", function(){
//object id = "element_" + id
var id = this.id.split("_")[1];
$.post("/deleteSpecificItem", { itemID: id }, function(data){
//Table of items with the row elements id = "tr_" + id
$("#tr_" + id).remove();
$.each(_viewModel.SpecificItem.Members, function(index, value){
if(value.ID == id)
_viewModel.SpecificItem.Members.splice(index, 1);
});
$.each(_initData.SpecificItem.Members, function(index, value){
if(value.ID == id)
_initData.SpecificItem.Members.splice(index, 1);
});
});
});
});
我有一个足够复杂的对象,我不想为每个单独的属性添加处理程序。
实时对我的对象进行了一些更改,这些更改编辑了可观察对象和"_initData"。
当我从服务器获得数据时,我更新我的"_initData"对象,以尝试使其与服务器保持同步。
非常古老的问题,但我只是做了一些非常类似的事情,并找到了一个非常简单,快速,有效的方法来做到这一点,使用映射插件
的背景;我正在编辑使用foreach
绑定的KO对象列表。使用一个简单的可观察对象将每个对象设置为编辑模式,它告诉视图显示标签或输入。
这些函数被设计用于click
绑定每个foreach
项。
然后,编辑/保存/取消操作简单:
this.edit = function(model, e)
{
model.__undo = ko.mapping.toJS(model);
model._IsEditing(true);
};
this.cancel = function(model, e)
{
// Assumes you have variable _mapping in scope that contains any
// advanced mapping rules (this is optional)
ko.mapping.fromJS(model.__undo, _mapping, model);
model._IsEditing(false);
};
this.save = function(model, e)
{
$.ajax({
url: YOUR_SAVE_URL,
dataType: 'json',
type: 'POST',
data: ko.mapping.toJSON(model),
success:
function(data, status, jqxhr)
{
model._IsEditing(false);
}
});
};
这在编辑简单对象的列表时非常有用,尽管在大多数情况下,我发现自己有一个包含轻量级对象的列表,然后为实际编辑加载一个完整的细节模型,所以这个问题不会出现。
你可以添加saveUndo
/restoreUndo
方法到模型,如果你不喜欢像那样添加__undo
属性,但我个人认为这种方式更清晰,以及代码少得多,可用于任何模型,甚至没有显式声明。
您可以考虑使用KO-UndoManager。下面是注册视图模型的示例代码:
viewModel.undoMgr = ko.undoManager(viewModel, {
levels: 12,
undoLabel: "Undo (#COUNT#)",
redoLabel: "Redo"
});
你可以在html中添加撤销/重做按钮,如下所示:
<div class="row center-block">
<button class="btn btn-primary" data-bind="
click: undoMgr.undoCommand.execute,
text: undoMgr.undoCommand.name,
css: { disabled: !undoMgr.undoCommand.enabled() }">UNDO</button>
<button class="btn btn-primary" data-bind="
click: undoMgr.redoCommand.execute,
text: undoMgr.redoCommand.name,
css: { disabled: !undoMgr.redoCommand.enabled() }">REDO</button>
</div>
这是一个普朗克展示它的行动。要撤消所有更改,您需要在javascript中循环调用undoMgr.undoCommand.execute
,直到撤消所有更改。
我需要类似的东西,但我不能使用受保护的可观察对象,因为我需要计算来更新临时值。所以我写了这个knockout扩展:
这个扩展为每个可观察对象创建一个下划线版本,即self.Comments() -> self._Comments()
ko.Underscore = function (data) {
var obj = data;
var result = {};
// Underscore Property Check
var _isOwnProperty = function (isUnderscore, prop) {
return (isUnderscore == null || prop.startsWith('_') == isUnderscore) && typeof obj[prop] == 'function' && obj.hasOwnProperty(prop) && ko.isObservable(obj[prop]) && !ko.isComputed(obj[prop])
}
// Creation of Underscore Properties
result.init = function () {
for (var prop in obj) {
if (_isOwnProperty(null, prop)) {
var val = obj[prop]();
var temp = '_' + prop;
if (obj[prop].isObservableArray)
obj[temp] = ko.observableArray(val);
else
obj[temp] = ko.observable(val);
}
}
};
// Cancel
result.Cancel = function () {
for (var prop in obj) {
if (_isOwnProperty(false, prop)) {
var val = obj[prop]();
var p = '_' + prop;
obj[p](val);
}
}
}
// Confirm
result.Confirm = function () {
for (var prop in obj) {
if (_isOwnProperty(true, prop)) {
var val = obj[prop]();
var p = prop.replace('_', '');
obj[p](val);
}
}
}
// Observables
result.Properties = function () {
var obs = [];
for (var prop in obj) {
if (typeof obj[prop] == 'function' && obj.hasOwnProperty(prop) && ko.isObservable(obj[prop]) && !ko.isComputed(obj[prop])) {
var val = obj[prop]();
obs.push({ 'Name': prop, 'Value': val });
}
}
return obs;
}
if (obj != null)
result.init();
return result;
}
这个扩展将节省你写你的每个可观察对象的副本,并忽略你的计算。它是这样工作的:
var BF_BCS = function (data) {
var self = this;
self.Score = ko.observable(null);
self.Comments = ko.observable('');
self.Underscore = ko.Underscore(self);
self.new = function () {
self._Score(null);
self._Comments('');
self.Confirm();
}
self.Cancel = function () {
self.Pause();
self.Underscore.Cancel();
self.Resume();
}
self.Confirm = function () {
self.Pause();
self.Underscore.Confirm();
self.Resume();
}
self.Pause = function () {
}
self.Resume = function () {
}
self.setData = function (data) {
self.Pause();
self._Score(data.Score);
self._Comments(data.Comments);
self.Confirm();
self.Resume();
}
if (data != null)
self.setData(data);
else
self.new();
};
如你所见,如果你在html:
上有按钮<div class="panel-footer bf-panel-footer">
<div class="bf-panel-footer-50" data-bind="click: Cancel.bind($data)">
Cancel
</div>
<div class="bf-panel-footer-50" data-bind="click: Confirm.bind($data)">
Save
</div>
</div>
Cancel将撤销并恢复你的可观察对象到它们原来的样子,因为save将用一行中的临时值更新实际值
- 在VanillaJS中模拟模型双向数据绑定
- 为集合分配大量的模型弹药
- 如何使用backbone.js从集合中获取模型名称
- 骨干模型默认值-todos.js示例中不必要的代码
- EmberJS中支持单字母单词模型
- 2个backbone.js集合,具有相同的模型,但排序顺序不同
- 将不在模型中的数据返回到mvc控制器
- 如何在视图模型contet更新更新上调用Jquery函数
- 如何使用Javascript客户端对象模型检索Sharepoint 2010列表项权限
- 我应该如何检查主干.主干.模型更改时查看
- Ext.js从json构建模型关系的问题
- 显示模块模式在Knockout中设置模型的新实例
- 使用导航属性创建Kendo UI网格模型的问题
- Angular,函数在(模型)工厂中返回值
- 如何将本地json数据加载到Extjs数据模型中
- 有没有比在app.js上绑定模块名称更好的方法来动态加载视图模型和视图以显示模态
- 创建主干模型的副本
- 在AngularJS中保存模型的静态副本
- 骨干模型及其原型链的深度副本
- 如何取消/恢复对可观察模型的更改(或用未修改的副本替换数组中的模型)