w3schools knockout item example javascript knockout.js observable

knockout - ko javascript



Cómo cancelar/revertir los cambios a un modelo observable(o reemplazar el modelo en una matriz con una copia intacta) (5)

Tengo un modelo de vista con un conjunto observable de objetos con variables observables.

Mi plantilla muestra los datos con un botón de edición que oculta los elementos de visualización y muestra los elementos de entrada con los valores encuadernados. Puede comenzar a editar los datos y luego tiene la opción de cancelar. Me gustaría que esta cancelación revierte a la versión sin modificar del objeto.

He intentado clonar el objeto haciendo algo como esto:

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

o

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

pero viewModel.tempContact se modifica tan pronto como lo hace el contacto.

¿Hay algo incorporado en KnockoutJS para manejar este tipo de situación o es mejor que simplemente cree un nuevo contacto con exactamente los mismos detalles y reemplace el contacto modificado con el nuevo contacto al cancelar?

Cualquier consejo es muy apreciado. ¡Gracias!


Hay algunas maneras de manejar algo como esto. Puede construir un nuevo objeto con los mismos valores que su actual y tirarlo en una cancelación. Puede agregar observables adicionales para enlazar a los campos de edición y persistir en la aceptación o echar un vistazo a esta post para obtener una idea sobre cómo encapsular esta funcionalidad en un tipo reutilizable (este es mi método preferido).


Me encontré con este post mientras buscaba resolver un problema similar y pensé que publicaría mi enfoque y solución para el siguiente tipo.

Seguí tu línea de pensamiento: clona el objeto y repobla con datos antiguos en "deshacer":

1) Copie el objeto de datos en una nueva variable de página ("_initData") 2) Cree Observable desde el objeto servidor original 3) en "deshacer" vuelva a cargar observable con datos inalterados ("_initData")

Simplified 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); }); }); }); });

Tenía un objeto que era lo suficientemente complicado como para no querer agregar controladores para cada propiedad individual.

Algunos cambios se realizan en mi objeto en tiempo real, esos cambios editan tanto el observable como el "_initData".

Cuando recibo datos del servidor, actualizo mi objeto "_initData" para intentar mantenerlo sincronizado con el servidor.


Necesitaba algo similar, y no podía usar los observables protegidos, ya que necesitaba el calculado para actualizar los valores temporales. Así que escribí esta extensión nocaut:

Esta extensión crea una versión de subrayado de cada elemento observable, es decir, 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; }

Esta extensión le ahorrará escribir duplicados de cada uno de sus observables e ignorará su cálculo. Funciona así:

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(); };

Entonces, como puedes ver si tienes botones en 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>

Cancelar deshará y revertirá sus observables a lo que eran, al igual que guardar actualizará los valores reales con los valores temp en una línea


Pregunta muy antigua, pero hice algo muy similar y encontré una manera muy simple, rápida y efectiva de hacer esto usando el plugin de mapeo.

Fondo; Estoy editando una lista de objetos KO enlazados usando un foreach . Cada objeto está configurado para estar en modo de edición usando un simple observable, que le dice a la vista que muestre etiquetas o entradas.

Las funciones están diseñadas para ser utilizadas en el enlace de click para cada elemento foreach .

Entonces, editar / guardar / cancelar es simplemente:

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); } }); };

Esto es muy útil cuando edito listas de objetos simples, aunque en la mayoría de los casos me encuentro con una lista que contiene objetos livianos, luego cargando un modelo completo de detalles para la edición real, por lo que este problema no aparece.

Podría agregar métodos saveUndo / restoreUndo al modelo si no le gusta __undo propiedad __undo así, pero personalmente creo que de esta manera es más claro, además de ser mucho menos código y utilizable en cualquier modelo, incluso uno sin un declaración explícita.


Puede considerar usar KO-UndoManager para esto. Aquí hay un código de muestra para registrar su modelo de vista:

viewModel.undoMgr = ko.undoManager(viewModel, { levels: 12, undoLabel: "Undo (#COUNT#)", redoLabel: "Redo" });

A continuación, puede agregar botones deshacer / rehacer en su html de la siguiente manera:

<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>

Y here está un Plunkr mostrándolo en acción. Para deshacer todos los cambios necesitará hacer una llamada de bucle a undoMgr.undoCommand.execute en javascript hasta que todos los cambios se deshagan.