In solchen Situationen habe ich immer festgestellt, dass Staatsmaschinen der richtige Weg sind. Gestalten Sie Ihre Seite/Ihr JavaScript so, dass Sie zu jedem Zeitpunkt eine Reihe von Eingaben vornehmen können und Sie immer die gleiche Ausgabe über die gesamte Seite erhalten. Das Schöne an diesem Ansatz ist, dass Sie nicht wissen, wie Sie, wo Sie in Ihrem Programm sind, müssen Sie nur wissen, welche Daten in den aktuellen Zustand führte.
Für Ihren Fall könnten Sie ein grundlegendes Objekt beschreiben Ihren Zustand erstellen:
var state = { user: 'Tomasz', ajaxSent: false, otherState: 123 };
function updateFromState(state){
// Set all dependent UI visibilities, etc. based on state object
}
Dann, bevor Sie Änderungen vornehmen, ihren aktuellen Zustand auf ein Array schieben, so dass Sie wieder bei Bedarf rückgängig machen können:
var stateList = [];
stateList.push({ user: 'Tomasz', ajaxSent: false, otherState: 123 });
// Do something that changes your state
var newState = {user: 'Tomasz', ajaxSent: true, otherState: 123 };
updateFromState(newState);
// Now, if you need to revert
var previousState = stateList.pop();
updateFromState(previousState);
Der Nachteil dieses Ansatzes ist, dass Sie keine Nebenwirkungen "rückgängig machen", die Sie möglicherweise aufgerufen haben. Wenn Sie zum Beispiel einen AJAX-Post erstellt haben, der Daten auf dem Server geändert hat, müssten Sie verstehen, wie Sie diesen AJAX-Post rückgängig machen können. Dies geht über das Ändern des lokalen Status der Seite hinaus. Wenn Sie serverseitige Effekte nicht rückgängig machen müssen, sondern nur den korrekten Status der Seite wiedergeben können, funktioniert dies möglicherweise für Sie. Hier
ist, was würde ich wahrscheinlich um loszulegen verwenden, wenn ich so etwas wie dies tat:
var StateHelper = function(){
var stateList = [];
var callbacks = [];
this.onChange = function(callback){
callbacks.push(callback);
};
this.set = function(opts){
stateList.push(opts);
apply(opts);
};
this.undo = function(){
if(stateList.length <= 1){
return; // nothing to undo
}
// To undo, we go back 2, since top of stack is current
stateList.pop();
var last = stateList[stateList.length - 1];
apply(last);
};
var apply = function(opts){
var length = callbacks.length;
for(var i = 0; i < length; i++){
callbacks[i](opts);
}
};
};
Wenn dann meine Seite sieht wie folgt aus:
<div id='title' style='text-decoration: underline;'>some title</div>
<div id='state1' class='clickable'>click to set state2 (title = 'second')</div>
<div id='state2' class='clickable'>click to set state3 (title = 'third')</div>
<br/>
<div id='undo' class='clickable'>undo</div>
Ich könnte die oben verwenden StateHelper wie folgt (man beachte ich einen Hauch von jQuery verwendet):
// A function that should be called when state changes
var updateTitle = function(opts){
// Update the pages title based on state
console.log('title updating');
$('#title').text(opts.title);
};
var myState = new StateHelper();
myState.onChange(updateTitle); // hook the state change event
// some example states
var state2 = { title: 'second' };
var state3 = { title: 'third' };
// Just some click handlers
$(document).ready(function(){
$('#state1').click(function(){
myState.set(state2);
});
$('#state2').click(function(){
myState.set(state3);
});
$('#undo').click(function(){
myState.undo();
});
// Set initial state
myState.set({title: 'some title'});
});
Für Live-Beispiel finden Sie unter: http://jsfiddle.net/belorion/Hywtc/
+1 für die sehr schön Antwort - Ich fühlte, dass mir eine so einfache Lösung fehlte. Tatsächlich bin ich hauptsächlich damit beschäftigt, DOM-Änderungen nach einer fehlgeschlagenen AJAX-Anfrage rückgängig zu machen, so dass keine serverseitigen Änderungen rückgängig gemacht werden können. –
@Tomasz aktualisiert mit einem Code – Matt
Ich bin noch nicht fertig mit all dem Zeug, aber es sieht aus wie eine Lösung für mein Problem, also akzeptiere ich Ihre Antwort jetzt. Vielen Dank! –