2011-05-03 55 views
23

我有一个带有可观察变量的对象的observableArray的viewModel。如何取消/恢复对可观察模型的更改(或用未更改的副本替换模型)

我的模板显示的数据带有一个编辑按钮,它隐藏了显示元素并显示了绑定值的输入元素。您可以开始编辑数据,然后您可以选择取消。我希望取消这个对象的原始版本。

我已经做这样的事情试图克隆对象:

viewModel.tempContact = jQuery.extend({}, contact); 

viewModel.tempContact = jQuery.extend(true, {}, contact); 

但viewModel.tempContact得到尽快联系不修改。

KnockoutJS有什么内置的处理这种情况,或者我最好只创建一个具有完全相同细节的新联系人,并用取消的新联系人替换已修改的联系人?

任何意见非常感谢。谢谢!

回答

16

有几种方法来处理这样的事情。您可以使用与当前值相同的值构造一个新对象,并在取消时将其丢弃。您可以添加额外的observables来绑定到编辑字段并将它们保留在accept上,或者查看此post以获取将此功能封装为可重用类型(这是我的首选方法)的想法。

+0

谢谢,瑞安!我今天在你的网站上看过几篇文章,但不知何故,我错过了那篇文章。好东西! – gabrielsond 2011-05-03 20:18:42

+0

谢谢!我现在使用这个代码,它工作得很好。我进行了修改以使临时值可观察。我还添加了一个计算来检查protectedObservable是否脏。 – Gunslinger 2013-08-21 13:15:12

3

我跑过这篇文章,同时寻找解决类似的问题,并认为我会张贴我的方法和解决方案的下一个人。

我就跟着你的思路 - 克隆对象,并与“撤销”旧数据重新填充:

1)数据对象复制到一个新的页面变量(“_initData”) 2)创建可观测从原始服务器对象 3)在“撤消”重新加载可观察到的未改变的数据(“_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); 
      }); 
     }); 
    }); 
}); 

我有一个足够复杂的对象,我不想为每个单独的属性添加处理程序。

实时对我的对象进行了一些更改,这些更改同时编辑了observable和“_initData”。

当我从服务器获取数据时,我更新我的“_initData”对象以尝试使其与服务器保持同步。

0

您可能会考虑为此使用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> 

而且here的一个Plunkr显示在行动了。要撤消所有更改,您需要在JavaScript中循环调用undoMgr.undoCommand.execute,直到所有更改都被撤消。

2

非常古老的问题,但我只是做了非常相似的事情,并找到了一个非常简单,快速和有效的方式来使用映射插件来完成此操作。

背景;我在编辑一个使用foreach绑定的KO对象列表。每个对象都被设置为使用简单的可观察模式处于编辑模式,这会告诉视图显示标签或输入。

该功能旨在用于每个foreach项目的click绑定。

然后,编辑/保存/取消很简单:

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性质类似的,但我个人认为这种方式更清晰,以及作为少了很多代码,并可以通过任何模型,即使没有明确的声明。

0

我需要类似的东西,我不能使用受保护的observables,因为我需要计算更新临时值。所以我写了这个淘汰赛扩展:

这个扩展创建每个可测量即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> 

取消将取消,并恢复您的观测回到它们是什么,因为是保存将更新实际值与温度值在一行