2

I'm developing an experimental application for work that requires fully persistent undos and redos. I decided to use react/redux/immutable JS tech stack to solve this problem because I saw it worked out nicely.

I'm actually really happy with the simplicity of the solution but my issue with this decision is that this architecture and tech-stack deviates from what the team currently uses which is an angular/polymer/vue style two-way data-binding architecture.

So in an effort to remain open-minded:

Is undo and redo possible/feasible with two-way data binding when compared uni-directional data flow and fully persistent data structures?

Why or why not? How do you achieve fully-persistent undos/redos with two-way data binding?

Edit:

From my comment below:

I am concerned with recording any change to the data model so the user can undo those. I have personally found success in doing so using immutable persistent data structures with 'uni-directional data flow' because I know where changes come in and where to save previous models. I can then emit a previous model and have react re-render when an undo action occurs. Now what I want to know is how to implement undos and redos that can track any change and undo it using two-way data binding architecture instead. I'm open to any solution that solves this problem using two-way data binding

  • I have implemented undo-redo with persistence, using knockout's 2 way binding. Users see it as undo and redo options that reverse and replay a series of edits. This is in an interactive editor for fairly arbitrary XML documents. 2-way binding was not an obstacle, and was somewhat helpful in the process, It could be done just as well with 1-way binding. Are you concerned about missing changes or recording extra changes because they are detected and captured by the 'black box' of 2-way binding? What am I missing in this question? – joshp May 1 '17 at 16:36
  • @joshp I am concerned with recording any change to the data model so the user can undo those. I have personally found success in doing so using immutable persistent data structures with 'uni-directional data flow' because I know where changes come in and where to save previous models. I can then emit a previous model and have react re-render when an undo action occurs. Now what I want to know is how to implement undos and redos that can track any change and undo it using two-way data binding architecture instead. I'm open to any solution that solves this problem using two-way data binding. – Rico Kahler May 1 '17 at 17:13
  • I don't think two-way data binding is appropriate in this case. Undo and Redo imply that the changes are only going in one direction. – Frank Hileman May 1 '17 at 21:51
  • 1
    @RicoKahler Ok. I think I get it now. Yes the undo-redo log entries are immutable, or it doesn't work. But this does not have to have any effect on binding from model to DOM. I don't bind the undo log to the DOM. I just play it into the model forward or reverse. There is no general right answer to this. Best answer depends on what you are editing and what operations you need to undo and redo. I think there is an interesting analysis of the impacts of 1-way or 2-way binding. Question is how to write that as an answer. – joshp May 1 '17 at 22:43
3
+100

Typically you implement an undo/redo feature using a command history. Your UI generates commands with execute and unexecute actions, and the underlying model is changed by playing the command's execute action on it. Then navigating the undo/redo history is just performing execute or unexecute actions on the "current" command and updating the history's pointer to the appropriate command. This means that you do not store complete models for previous entries, just the command that produced them from the previous state.

In one-way data-binding the appropriate way to implement is by having the UI generate the commands directly and then executing them on the underlying model layer. In a two-way data binding it can be a bit more awkward, but you can still generate the command history from events on the model layer, and then do some bookkeeping in those event handlers to prevent them from firing when you are navigating the command history and (un)executing the commands to update the model.

Either way, what you do not need to do is store previous models in their entirety. You only need the delta between states, and that is represented best by the command that generated the change.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.