Posted by & filed under User Interface.

I was on a project recently that had the business requirement to automatically save data as it was entered.  This can be accomplished be hooking into the onChange event of your ui controls.

When you add Knockout into the mix you can get some very nice functionality with very little effort.

First thing we need is a form.  (note: this form is marked up with Twitter Bootstrap classes.)

<form class="well">
  <fieldset>
   <legend>Auto Save Form</legend>
   <div class="control-group">
      <label class="control-label">First Name</label>
      <input type="text" data-bind="value: FirstName, event: { change: saveForm }"/>
   </div>
   <div class="control-group">
      <label class="control-label">Last Name</label>
      <input type="text" data-bind="value: LastName, event: { change: saveForm }"/>
   </div>
   <div class="control-group">
      <label class="control-label">Email</label>
      <input type="text" data-bind="value: Email, event: { change: saveForm }"/>
   </div>
  </fieldset>
</form>

This form includes both the Knockout js file as well as the Knockout mapping plugin.

In the JavaScript the first thing we do is setup our observables.  Many times I will will use the knockout mapping plugin to setup my viewModel.  Another technique that can be improve page loading times is to retrieve the viewModel data via an ajax call to the server while displaying some sort of “loading” indicator.   For this demo however I will keep it simple.

   var viewModel = {
      FirstName: ko.observable(''),
      LastName: ko.observable(''), 
      Email: ko.observable('')
   };
   ko.applyBindings(viewModel);

Now all we need is the saveForm function.  Knockout will pass two parameters to this function “data” and “event”.  This is well documented here.

The “event” parameter is the DOM event object.  This is very useful if you need to have a reference to the control that fired the event.  That can be retrieved using event.target.

In this example I will only use the “data” parameter.  The “data” parameter is always the parent object of the element the control is bound to.  In this case that is the viewModel object.  Inside a foreach or with binding this could be a child or other descendent of the viewModel.

The saveForm function uses the jQuery ajax function to post our viewModel to the server.

   function saveForm(data, event) {
      $.ajax({
         type: "POST",
         url: '/SaveForm',
         data: ko.mapping.toJS(data),
         success: function(result) {
            if (result) {
               //update viewModel with any changes from the server
               notify(result.Message, 'success');
               for (var prop in result) {
                  if (prop != 'message' && data[prop]() != result[prop])
                     data[prop](result[prop]);
               }
            } else
               notify("Changes Failed", 'error');
         },
         error: function(req, status, error) {
            notify("Changes Failed", 'error');
         }
      });
   }

The first thing to notice is how the viewModel is converted to a JavaScript object using the knockout mapping plugin – ko.mapping.toJS(data).

In this example the server will pass back a JSON object that almost matches our viewModel structure with the exception of a property called “Message”.  Because the result object matches our viewModel we can loop through the properties of the result object and update our viewModel will any values that were changed on the server.  This technique can be very useful if you want to compute values in your viewModel on the server and update them client side.

That’s pretty much it for the purposes of auto saving.

You can see a working demo and download the source files here.

Lastly I have a notify function that wraps a call to the noty jQuery plugin.  This is a nice plugin for notifications that I have been using lately.

Here is the notify function.

function notify(message, type) {
      noty({
         "text": message,
         "theme": "noty_theme_twitter",
         "layout": "top",
         "type": type,
         "animateOpen": { "height": "toggle" },
         "animateClose": { "height": "toggle" },
         "speed": 500,
         "timeout": 5000,
         "closeButton": false,
         "closeOnSelfClick": true,
         "closeOnSelfOver": false,
         "modal": false
      });
   }
  • Daniel S

    I’m implementing something similar and am curious how you dealt with the case where a user edits a field and closes the tab/browser before the onchange event fires? Was this edge case even a concern? Also, I’ve seen similar cases where a link is clicked on the page and the page transition occurs before the onchange->ajax chain completes. Any insight into this and how you handled this from a usability perspective would be greatly appreciated!

    • ripryness

      First let me say that no where I have implemented this sees a lot of activity, so I can’t say it is well tested in production.

      With that said, your concerns have not come up as issues for me. But if they did I’d probably start by looking at doing something with onbeforeunload.

  • Kaah

    Please fix the http://demo.ripryness.com/Demos/AutoSaveForm link. It is broken.

  • Shakti

    Hi, please fix the link http://demo.ripryness.com/Demos/AutoSaveForm its broken