June 2012

Volume 27 Number 06

Data Points - Data Bind OData in Web Apps with Knockout.js

By Julie Lerman | June 2012

Julie Lerman
As a data geek, I spend quite a lot of time writing back-end code and miss out on a lot of the fun stuff on the client side. John Papa, who used to pen this column, is now writing a column on client technologies for this magazine, and he’s been doing a lot of work on a hot new client-side technology called Knockout.js. Thanks to the passion that Papa and others have been expressing about Knockout.js, I jumped at an offer for a presentation on Knockout at my local user group, VTdotNET, by Jon Hoguet from MyWebGrocer.com. The meeting drew a larger crowd than we typically get, and included devs from outside the .NET community. As Hoguet worked his way through the presentation, it became obvious to me why so many Web developers are enticed by Knockout: It simplifies client-side data binding in Web applications by leveraging the Model-View-ViewModel (MVVM) pattern. Data binding … Now that’s something I can do with data! The next day I was already learning how to weave Knockout.js with data access—and now I can share my discoveries with you.

I should provide fair warning that my JavaScript skills are terribly lacking, so this took me a bit longer to work out than it ought to have. However, I’m guessing that many of you who read this column are in the same boat, so you just might appreciate my baby steps. You can get a much deeper understanding of Knockout from Papa’s columns, as well as his excellent course on Pluralsight.com.

My goal was to see how I could use Knockout.js to bind to and then update data retrieved from a WCF Data Service. What I’ll do in this article is show you the critical working parts. Then you can see how they all fit together in the accompanying download sample.

I began with an existing WCF Data Service. In fact, it’s the same service I used in my December 2011 Data Points column, “Handling Entity Framework Validations in WCF Data Services” (msdn.microsoft.com/magazine/hh580732), which I’ve now updated to the recently released WCF Data Services 5.

As a reminder, my demo-ware model comprised a single class:

public class Person
{
  public int PersonId { get; set; }
  [MaxLength(10)]
  public string IdentityCardNumber { get; set; }
  public string FirstName { get; set; }
  [Required]
  public string LastName { get; set; }
}

A DbContext class exposed the Person class in a DbSet:

public class PersonModelContext : DbContext
{
  public DbSet<Person> People { get; set; }
}

The Data Service then exposed that DbSet for reading and writing:

public class DataService : DataService<PersonModelContext>
{
  public static void InitializeService(DataServiceConfiguration config)
  {
    config.SetEntitySetAccessRule("People", EntitySetRights.All);
  }
}

With Knockout.js, a client-side script can respond to changes in the property values of data-bound objects. For example, if you call the Knockout applyBindings method in your script, passing in an object, Knockout will alert any data-bound element of updates to that object’s properties. You can achieve similar behavior for collections that Knockout is watching as they acquire or eject items. All of this happens in the client with no need to write any event-handling script or return to the server for help.

There are a number of tasks I had to perform to achieve my goal:

  • Create Knockout-aware view models.
  • Get OData in JSON format.
  • Move the OData results into my view model object.
  • Bind the data with Knockout.js.
  • For updates, move the view model values back into the OData result object.

Creating Knockout-Aware View Models

In order for this to work, Knockout needs to be able to “observe” the properties of that object. You can enable this using Knockout when you define the properties in your object. But wait! I’m not suggesting that you “dirty up” your domain objects with UI-specific logic so that Knockout can observe value changes. This is where the MVVM pattern comes in. MVVM lets you create a UI-specific version (or view) of your model—that’s the VM (ViewModel) of MVVM. This means you can pull your data into your app however you like (querying against WCF Data Services, hitting a service or even via the new Web API), then reshape the results to align to your view. For example, my data service returns Person types with a FirstName, LastName and IdentityCard. But in my view, I’m only interested in FirstName and LastName. You can even apply view-specific logic in the view model version of your object. This gives you the best of both worlds: you get an object that’s specifically targeted to your view, regardless of what the data source provides.

Here’s the client-side PersonViewModel that I’ve defined in a JavaScript object:

function PersonViewModel(model) {
  model = model || {};
  var self = this;
  self.FirstName = ko.observable(model.FirstName || ' ');
  self.LastName = ko.observable(model.LastName || ' ');
}

No matter what’s returned from the service, I only want to use the first and last name in my view, so these are the only properties it contains. Notice that the names aren’t defined as strings but as Knockout observable objects. This is important to keep in mind when setting values, as you’ll see further on.

Getting OData in JSON Format

The OData query I’ll use will simply return the first Person from the data service. Currently, that’s coming from my development server:

https://localhost:43447/DataService.svc/People?$top=1

By default, OData results are returned as ATOM (which is expressed using XML). Knockout.js works with JSON data, though, which OData can also provide. Therefore, because I’m working directly in JavaScript, it’s a lot simpler to deal with JSON results than with XML. In a JavaScript request, you can append a parameter to the OData query to specify that the results are returned as JSON: “$format=json.” But this requires that your particular data service knows how to process the format query option. Mine doesn’t. If I want to go this route—for example, if I’m using AJAX to make my OData calls—I have to use an extension in my service to support the JSON output (see bit.ly/mtzpN4 for more information).

However, because I’m using the datajs toolkit for OData (datajs.codeplex.com), I don’t need to be concerned with this. The toolkit’s default behavior is to automatically add header information to requests so that they’ll return JSON results. So I won’t need to add the JSONP extension to my data service. The OData object from the datajs toolkit has a read method that lets you execute a query whose results will be in JSON format:

OData.read({
  requestUri: https://localhost:43447/DataService.svc/People?$top=1"
  })

Pushing OData into PersonViewModel

Once the results have been returned—in my case a single Person type as defined by my domain model—I then want to create a PersonViewModel instance from the result. My JavaScript method, personToViewModel, takes a Person object, creates a new PersonViewModel from its values and then returns the PersonViewModel:

function personToViewModel(person) {
  var vm=new PersonViewModel;
  vm.FirstName(person.FirstName);
  vm.LastName(person.LastName);
  return vm;
}

Notice that I’m setting the values by passing in the new values as though the properties are methods. Originally, I set the values using vm.FirstName=person.FirstName. But that turned FirstName into a string, rather than an observable. I struggled for a while, trying to see why Knockout wasn’t noticing subsequent changes to the value, and finally had to give in and ask for help. The properties are functions, not strings, so you need to set them using method syntax.

I want to run personToViewModel in response to the query. This is possible because OData.read lets you tell it what callback method to use when the query has executed successfully. In this case, I’ll pass the results to a method called mapResultsToViewModel, which, in turn, calls personToViewModel (see Figure 1). Elsewhere in the solution, I’ve predefined the peopleFeed variable as “https://localhost:43447/DataService.svc/People.”

Figure 1 Executing a Query and Handling the Response

OData.read({
  requestUri: peopleFeed + "?$top=1"
  },
  function (data, response) {
    mapResultsToViewModel(data.results);
  },
  function (err) {
    alert("Error occurred " + err.message);
  });
  function mapResultsToViewModel(results) {
    person = results[0];
    vm = personToViewModel(person)
    ko.applyBindings(vm);
}

Binding to HTML Controls

Notice the code in the mapResultsToViewModel method: ko.applyBindings(vm). This is another key aspect of how Knockout works. But what am I applying bindings to? That’s what I’ll define in my markup. In my markup code, I use the Knockout data-bind attribute to bind the values from my PersonViewModel to some input elements:

<body>
  <input data-bind="value: FirstName"></input>
  <input data-bind="value: LastName"></input>
  <input id="save" type="button" value="Save" onclick="save();"></input>
</body>

If I only wanted to display the data, I could use label elements and instead of data binding to the value, I could bind to text. For example:

<label data-bind="text: FirstName"></label>

But I want to edit, so I’m not only using an input element. My Knockout data bind also specifies that I’m binding to the value of these properties.

The key ingredients that Knockout is providing to my solution are the observable properties in my view model, the data-bind attribute for my markup elements, and the applyBindings method that adds the runtime logic necessary for Knockout to notify those elements when a property value changes.

If I run what I have so far in the application, I can see the person returned by the query in debug mode, as shown in Figure 2.

Person Data from the OData Service
Figure 2 Person Data from the OData Service

Figure 3shows the PersonViewModel property values displayed on the page.

The PersonViewModel Object Bound to Input Controls
Figure 3 The PersonViewModel Object Bound to Input Controls

Saving Back to the Database

Thanks to Knockout, when it’s time to save I don’t have to extract the values from the input elements. Knockout has already updated the PersonViewModel object that was bound to the form. In my save method I’ll push the PersonViewModel values back to the person object (that came from the service) and then save those changes back up to the database by way of my service. You’ll see in the code download that I kept the person instance that was originally returned from the OData query and I’m using that same object here. Once I update person with the viewModeltoPerson method, I can then pass it into the OData.request as part of a request object, as shown in Figure 4. The request object is the first parameter and consists of the URI, the method and the data. Take a look at the datajs documentation at bit.ly/FPTkZ5 to learn more about the request method. Notice I’m leveraging the fact that the person instance stored the URI it’s bound to in the __metadata.uri property. With that property, I won’t have to hardcode the URI, which is “https://localhost:43447/DataService.svc/People(1).”

Figure 4 Saving Changes Back to the Database

function save() {
  viewModeltoPerson(vm, person);
  OData.request(
    {
      requestUri: person.__metadata.uri,
      method: "PUT",
      data: person
    },
    success,
    saveError
    );
  }
  function success(data, response) {
    alert("Saved");
  }
  function saveError(error) {
    alert("Error occurred " + error.message);
  }
}
function viewModeltoPerson(vm,person) {
  person.FirstName = vm.FirstName();
  person.LastName = vm.LastName();
}

Now when I modify the data—by changing Julia to Julie, for example—and hit the Save button, not only do I get a “Saved” alert to indicate that no errors occurred, but I can see the database update in my profiler:

exec sp_executesql N'update [dbo].[People]
set [FirstName] = @0
where ([PersonId] = @1)
',N'@0 nvarchar(max) ,@1 int',@0=N'Julie',@1=1

Knockout.js and WCF Data Services for the Masses

Exploring Knockout.js pushed me to learn some new tools that can be used by developers of any stripe, not just in the .NET platform. And while it did force me to exercise some rusty JavaScript skills, the code I wrote focused on the familiar task of object manipulation and not on the drudgery of interacting with the controls. It also led me down a path of architectural goodness, using the MVVM approach to differentiate my model objects from those I want to present in my UI. There’s certainly a lot more to what you can do with Knockout.js, especially for building responsive Web UIs. You can also use great tools like the WCF Web API (bit.ly/kthOgY) to create your data source. I look forward to learning more from the pros and finding other excuses to work on the client side.


Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other Microsoft .NET topics at user groups and conferences around the world. She blogs at thedatafarm.com/blog and is the author of “Programming Entity Framework” (O’Reilly Media, 2010) and “Programming Entity Framework: Code First” (O’Reilly Media, 2011). Follow her on Twitter at twitter.com/julielerman.

Thanks to the following technical experts for reviewing this article: John Papa and Alejandro Trigo