2011-05-03 22 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> 

取消將取消,並恢復您的觀測回到它們是什麼,因爲是保存將更新實際值與溫度值在一行