Saving data is simple, right? What app isn’t complete without some sort of saving mechanism? And while I’m at it, it’s nice to be able to track when changes occur so I can tell if a user can press the save button or even leave the current screen. Which bring me to making my buttons smart enough to know that they are already involved in a asynchronous process, so commanding would be nice. Finally, if I want to save data, it might be a good idea to validate the input on the client (and the server).
There are a lot of moving parts involved with saving changes, and I cover these in my new Pluralsight course “Single Page Apps with HTML5, ASP.NET Web API, Knockout, and jQuery”.
You can catch up on the previous posts in this series here:
More on the Code Camper SPA
Part 1 - The Story Begins (What is the Code Camper SPA?)
Part 2 - Client Technologies
Part 3 - Server Technologies (the Data Layer)
Part 4 - Serving JSON with ASP.NET Web API
Part 5 - HTML 5 and ASP.NET Web Optimization
Part 6 - JavaScript Modules
Part 7 - MVVM and KnockoutJS
Part 8 - Data Services on the Client
Part 9 - Navigation, Transitions, Storage and Messaging
Part 10 - Saving, Change Tracking, and Commanding
Part 11 - Responsive Design and Mobility
Change Tracking
Change tracking can help make a user’s experience better and frankly, can make a developer’s life a little easier. For example, change tracking makes it easier to disable and enable buttons for saving/canceling changes. If no changes have been made, the buttons should be disabled.
If changes have been made, then these buttons would be enabled.
We can also prevent users from navigating off the page until they have either saved or canceled their changes.
So how do you wire up change tracking with a SPA? The Code Camper SPA uses a custom change tracking tool that ties into KnockoutJS. With some special tips from the KnockoutJS creators (Ryan Niemeyer and Steve Sanderson) Hans Fjällemark and I wrote a component called the DirtyFlag. You can grab the DirtyFlag as part of the KoLite library on github or grab KoLite on NuGet.
How do we hook this up?
-
Pull KoLite into your project.
-
Wire up your model and tell it which properties you want to track changes on. (either pass an array of properties or the entire object)
Person = function () { var self = this; self.id = ko.observable(); self.firstName = ko.observable(); self.lastName = ko.observable(); self.dirtyFlag = new ko.DirtyFlag([ self.firstName, self.lastName); return self; };
- Create a computed property on your viewmodel and check if the model is dirty or not
isDirty = ko.computed(function() { return myPerson().dirtyFlag().isDirty(); })
Now I can check the isDirty property on the ViewModel to see if changes were made or not. I can bind this property to bindings in the View, such as buttons, too.
Commanding
When I’m about to take an asynchronous action that saves data across the wire, I want the user to know the action is still pending (via a activity indicator) and I want to prevent the button from being clicked again. The commanding technique works great for this. Built into KoLite is another component called asyncCommand which handles these situations.
So when a user clicks this button …
It should be disabled and show an activity indicator like this …
So how do you do this? Pull KoLite into your project and instead of writing a method that saves the data, wrap that in an ko.asyncCommand. Just like the ICommand interface in XAML, asyncCommand has 2 methods you can define: execute and canExecute.
saveCmd = ko.asyncCommand({ execute: function(complete) { $.when(datacontext.persons.updateData(speaker())) .always(complete); }, canExecute: function(isExecuting) { return !isExecuting && isDirty() && isValid(); } })
Simply use the execute for the logic that will perform the save and define the conditions when it is appropriate for the user to be able to execute the command. Then in the HTML set the binding to use the command and optionally, the activity indicator.
Validation
Validation is a huge part of a good user experience. Users expect to be told how you want the data entered. Nobody wants to enter garbage and have to wait to find out that it’s wrong … or worse, not tell them why its wrong (or even what is wrong).
HTML5 has some great validation support built in, but not everyone can take advantage of HTML5 just yet. So what do we do? This is one reason I like the Knockout.Validation library: it works well without HTML5, but works better with it. This library is written by Eric Barnard and is also available on github and NuGet.
For example, I can make a property required by extending the observable.
self.firstName = ko.observable().extend({ required: true });
You can test this validation out in the live Code Camper demo here.
More?
10 posts is good, but there’s one more to go. In the next and final post I’ll cover mobility and responsive web design.