Re: user operations undo or rollback feature?

152 views
Skip to first unread message

John Fletcher

unread,
Sep 14, 2013, 12:52:45 PM9/14/13
to der...@googlegroups.com

I don't know the answer because I'm not too good with DerbyJS yet,  but I must say, the question occurred to me too.

If you run the directory example project for example, it's basically CRUD "with a difference". The create page has a cancel option but the edit page doesn't. The save button on the edit page doesn't actually save, your changes are saved as you make them. Navigate away without saving and you've still saved.

Philosophically, working with an open mind, this actually seems to me a better way to work. But practically, such behaviour seems to contradict the entire history of GUI interfaces as I know it; certainly web based interfaces. So users are going to get a big shock and probably make some crucial mistakes with their data if you run your UI like that. Then they will get used to it of course, after making a mistake.

I'm wondering whether the DerbyJS community has considered this and has an answer or viewpoint on it.

On 14 Sep 2013 03:26, "Anthony Carnemolla" <tenc...@gmail.com> wrote:
Apologies in advance if I just suck at searching for this, but...

Is there a way or strategy in place to be able to undo an operation? Better yet, a series of operations?

The information is there (oplog?), but I'm not sure what the best way to approach this would be.

It would be quite handy to be able to undo an arbitrary number of operations performed by a user or group of users, etc. Perhaps, a sort of start transaction flag could be set prior to editing and then everything could be rolled back to that point in the log (but only for that user's operations) if needed. Maybe the entire operational transform process would have to be run again from that point (without the user's operations), or maybe there's some algorithm to disentangle it all in reverse. Or something else?

Did I miss this feature in the docs or this group's messages?

Without it, for anything in which the user should be able to cancel or undo an operation, I have to do a bunch of x-binding and storing of intermediate vars and all of that. It sure would be handy to be able to use the {} handlebars syntax instead.

--
You received this message because you are subscribed to the Google Groups "Derby" group.
To unsubscribe from this group and stop receiving emails from it, send an email to derbyjs+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Nate Smith

unread,
Sep 16, 2013, 2:35:08 PM9/16/13
to der...@googlegroups.com
The data necessary to undo operations on a given document is indeed stored in the journal. However, there is no interface to access these past operations or undo them from the model. Given that we do have the data, this would be a very cool feature to support, but it would be a significant amount of new work that we don't have prioritized yet.

To John's point, the directory example shows that it is possible to support both realtime updated and non-realtime updated UIs. The convention of a save button mostly came from technical limitations like writing a file to disk, form POST requests, and AJAX. Now that Derby and other realtime frameworks make it just as easy to write a UI backed by realtime data syncing, new UI paradigms will have to be established and users will have to get used to them gradually.

In the directory example, the label on the primary action button is what indicates how it works. The button on the "new" page says "Add person", whereas the edit page says "Done" instead of "Save". You'll also note that there is a "Cancel" button on the new page, since the action can be canceled, but there is no cancel on the edit page, since edits are made immediately. Google tends to flash the text "Saved" in Gmail and Google Docs to indicate that a document is updated immediately.

John Fletcher

unread,
Sep 16, 2013, 10:41:10 PM9/16/13
to der...@googlegroups.com
True, I didn't review the labels before writing. The Google flashing of "saved" is a very good feature to help users understand what's going on.


2013/9/16 Nate Smith <na...@nateps.com>
Message has been deleted

Anthony Carnemolla

unread,
Nov 1, 2013, 11:11:58 PM11/1/13
to der...@googlegroups.com
I've had some time to think about my original question (which I had deleted shortly after posting because I realized I needed to ponder it further. then, a reply appeared the next time I logged in. oh well)

I still think it would be a good thing to have access to all of the previous versions of a document. This goes beyond having an undo button. For instance, perhaps a supervisor role would like to review and accept or restore changes to a document. Or, as one of the things I'm aiming to do, track the lat/lon of an object as it's changing locations and see the path it's traveled by retrieving all previous points. In either case, extra copies of data can be saved, but the ops log is already storing the necessary info.

So, I would like to see if there's a way to access the ops log (get only, of course). I can see an ops log table in mongo, but I don't know how to get to it from my derby app. I would run into the client side, server side issue if I were to try to set up a second db connection inside the app. I suppose I can rig up an ajax call to an express route, but maybe there are plans to implement something soon? Maybe I should work harder on trying to learn the internals of racer/share/derby, and roll my own getOps function?

Curran Kelleher

unread,
Apr 28, 2016, 6:39:39 AM4/28/16
to Derby
Hello,

Is there a way to implement the Google-like flashing of "saved" with Derby? I'm wondering if there is a hook for listening for confirmation that an op was received by the server.

Also, for replay of history, check out the links from here https://github.com/VisionistInc/jibe/issues/7 . It appears someone solved it.

Best,
Curran

Carl-Johan Blomqvist

unread,
Apr 30, 2016, 3:47:27 AM4/30/16
to Derby
Regarding the save button pattern for edits. We created a library to handle this kind of CRUD stuff (including frontend validation): https://github.com/BBWeb/derby-validator

Curran Kelleher

unread,
Apr 30, 2016, 5:31:30 AM4/30/16
to der...@googlegroups.com
Thanks for the project link. It looks like an interesting project, but it's not clear to me how this can help to implement the "saved" feature. In my case, I'm looking to just tell the user that the OT sync has occurred. No validation is required. This seems like it should be a hook in Derby itself, something like "app.onSync". After the user types but before the data is committed, there could be text saying "saving...", and after the ops are submitted to the server, it could flash to "saved" and fade out.

On Sat, Apr 30, 2016 at 1:17 PM, Carl-Johan Blomqvist <carljohan...@gmail.com> wrote:
Regarding the save button pattern for edits. We created a library to handle this kind of CRUD stuff (including frontend validation): https://github.com/BBWeb/derby-validator

--
You received this message because you are subscribed to a topic in the Google Groups "Derby" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/derbyjs/IeaHLdXs7b0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to derbyjs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Carl-Johan Blomqvist

unread,
May 1, 2016, 12:37:41 PM5/1/16
to Derby
I don't know about that scenario actually. You could manually create the two-way sync and hook a callback into the model.set -call. That way you would be able to control whenever something is fully committed to your backend.

As for the save butt on problem, you can use derby-validator without any rules. The way it works, in short: When you create a "Validator object" what really happens is a local copy of all the data (for that item) is made. This is kept on your component's local data. This you can put in your view. Later, when you want to sync it back to your actual model data (and sync it to your backend/DB), you commit your data using the .commit() -method (which you can trigger when someone hits the Save-button). It's fairly well documented so this should fairly straight forward. It doesn't cover your scenario though.

It should also be able to modify Racer (or Derby) to create some kind of place to put such a hook you're describing, but that obviously becomes much more complicated. It's not obvious what you'd want to hook into. For example, you could hook into one specific template-binding, or a general path to some data in Racer. Both scenarios have various pro's and con's.

Ian Johnson

unread,
May 1, 2016, 12:51:19 PM5/1/16
to der...@googlegroups.com
I think Carl's suggestion to use the validator with local storage is wise. Racer's default mode is to send ops as soon as they happen, disconnects/fails will result in retries behind the scenes as long as the tab stays open. You can also detect disconnection and show a warning to the user. When the default is everything is automatically saved it becomes a subtly different experience for the user. You end up having to do a little more work (using the validator) to enable the usual ephemerality of form edits and introduce a save button.

--
You received this message because you are subscribed to the Google Groups "Derby" group.
To unsubscribe from this group and stop receiving emails from it, send an email to derbyjs+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Ian Johnson - 周彦

Curran Kelleher

unread,
May 3, 2016, 3:28:58 AM5/3/16
to Derby
Thanks Ian for your thoughts on this. I think detecting disconnection and show a warning to the user would be a great solution.

Curran Kelleher

unread,
Jun 1, 2016, 10:40:40 AM6/1/16
to Derby
Hi all,

I'm still really into the idea of implementing the Google-Docs-like "Saving..." -> "Saved" feature.

I've got a page where all editable fields map to properties under "_page.document", so here's the listener I can add in the client that should trigger the page to show "Saving...":

      model.on("change", "_page.document.**", function (){
        // show "Saving..." on the page
      });

Now, I'm searching for a way to listen for the fact that the updated data has in fact been sent to the server, which should trigger the page to show "Saved". At this point, there should be certainty that the data actually reached the server (I could fake it with a setTimeout, but that's lying to the user).

First of all, is this theoretically possible given the way Racer works? Is there any acknowledgement sent back to the client after data gets sent to the server? If so, how could one listen for the acknowledgement? Any ideas would be appreciated. Thank you.

Regards,
Curran

Rachael Stedman

unread,
Jun 1, 2016, 10:54:38 AM6/1/16
to Derby
Hi Curran,

I haven't tested this, but I'm pretty sure you could do this with `model.whenNothingPending`.


Here's it being used in a derby example (to wait for all changes to be saved to the server before leaving the page): https://github.com/derbyjs/derby-examples/blob/b00f216add6ca5d5e731e51ae85d7910bb77165f/directory/index.js#L84
 
Hope that helps!

Best wishes,
Rachael

Curran Kelleher

unread,
Jun 1, 2016, 12:43:39 PM6/1/16
to Derby
Hi Rachel,

Wow, thank you so much for your quick reply. I tried `model.whenNothingPending` and it works perfectly!

Here's the resulting code for the component:

module.exports = DocumentEditor;
function DocumentEditor(){}
DocumentEditor.prototype.view = __dirname;
  
DocumentEditor.prototype.create = function(){
  var model = this.model;
  var timeout; 
    
  var listener = model.on("change", "document.**", function (){
    model.set("_page.savingMessage", "Saving...");
    clearTimeout(timeout);
    model.whenNothingPending(function (){
      model.set("_page.savingMessage", "All changes saved.")
      timeout = setTimeout(function (){
        model.set("_page.savingMessage", "");
      }, 1000);
    });
  });
    
  this.on("destroy", function(){
    model.removeListener(listener);
  });

Here's the bit in the view that shows the message in the corner:

  <small class="text-muted pull-right">
    {{_page.savingMessage}}
  </small>

Thanks again, I really appreciate it. It's awesome to finally have this working.

Regards,
Curran

Curran Kelleher

unread,
Jun 15, 2016, 7:21:15 AM6/15/16
to Derby
Hi all,

It turns out that this solution has a bug in which the "All changes saved." message displays on remote clients as well, who are not editing.

Is there a way to know if the change was generated locally vs. remotely in the model change listener? I'd like to only show the "All changes saved." message in the client in which the edits took place, not remote clients. Thank you.

Best regards,
Curran
Reply all
Reply to author
Forward
0 new messages