Blog.

Knockout JS mappings

MF

Marco Franssen /

6 min read1114 words

Cover Image for Knockout JS mappings

Knockout JS is a JavaScript library for creating MVVM JavaScript libraries. In a previous post I already showed some of the cool features of Knockout.

http://marcofranssen.nl/knockout-that-cascading-dropdown/

If you want to spike your knowledge on Knockout a little more first, please visit Knockout's documentation.

In this article I want to zoom in on the Knockout mapping plugin. The Knockout mapping plugin enables you to easy map your JSON object into an observable JavaScript object. So here is a short example of what you'll be able to do with it. So when you do an ajax request and receive a JSON result you can for example do the following.

var viewModel;

// result could look like this: "{ "personId": 1, "firstName": "Marco", "lastName": "Franssen", "age": 26, "webpage": "http://marcofranssen.nl", "twitter": "@marcofranssen" }"
$.ajax({
    url: 'http://somewebpage.net/getPerson'
    type: 'GET',
    dataType: 'JSON',
    success: function (result) {
        var data = JSON.parse(result);
        viewModel = ko.mapping.fromJS(data);
        ko.applyBindings(viewModel, $('#person').get(0));
    },
    error: function (result) {
        //handle the error, left for brevity
    }
});

So what we are doing is making a request to some url which returns us a person JSON object. Then we parse the JSON to a regular JavaScript object, followed by mapping it into our view model. Last thing we do is binding the view model to the DOM nodes in our person DOM node (could be a div element or whatever). This creates the same view model object as we would do it by hand, like this.

var data = JSON.parse(result);
viewModel = {
  personId: ko.observable(data.firstName),
  firstName: ko.observable(data.firstName),
  lastName: ko.observable(data.lastName),
  age: ko.observable(data.age),
  webpage: ko.observable(data.webpage),
  twitter: ko.observable(data.twitter),
};

So the mapping plugin easily maps our JSON into an observable view model. But what if we want to skip some properties of our JSON or just don't want to wrap them into an observable. Well you definitely don't have to fall back on writing the object completely by hand. We can simply configure our mapping in an object and pass that into our call to ko.mapping.fromJS.

var mapping = {
  ignore: ["twitter", "webpage"],
  copy: ["age", "personId"],
  lastName: {
    create: function (options) {
      return ko.observable(options.data.toUpperCase());
    },
  },
};

viewModel = ko.mapping.fromJS(data, mapping);

By passing in the following mapping to the mapping plugin we tell the mapping plugin to ignore the twitter and webpage properties. We only want to copy the value of the personId and age property (Not wrap it in a ko.observable) and we configured our lastName property to be transformed to upper-case before putting it in an observable. When we would have done this by hand our code could have looked like this.

viewModel = {
  personId: data.personId,
  firstName: ko.observable(data.firstNametoUpperCase()),
  lastName: ko.observable(data.lastName),
  age: data.age,
};

Now you're maybe thinking why would I skip the observable wrapping for some properties…. The best reason to do this is when you know the property won't change (by user or update from server). Observables are quite expensive when you have a lot of them and require more memory. So when you have for example a big collection of persons in a table you could skip the observables for some properties to save memory and gain a small performance boost.

The mapping plugin can also be used when you get a collection of persons from the server. What you basically do is creating a view model and add the persons to the observable collection of your view model.

var viewModel = {
  persons: ko.observableArray(),
};

var persons = JSON.parse(jsonPersonArray);
for (person in persons) {
  viewModel.persons.push(person);
}

//Or even better performance wise (persons observableArray gets updated only once)
viewModel.persons.push.apply(viewModel.persons, persons);

With above example the person object is just a plain JavaScript object without observables. We can change that by creating another view model for each person. Probably you also want to check if the person isn't already in the observable array and so on. So imagine what amount of code you probably are going to put into the for loop. Luckily we have the mapping plugin.

First of all I will define a constructor / view model for my person objects. We just use the mapping defined before and adding a computed property to our view model by hand. You can of course add as many properties as you want by hand, to extend your person object with more functionality.

function PersonViewModel(data) {
  var personMapping = {
    ignore: ["twitter", "webpage"],
    copy: ["age"],
    lastName: {
      create: function (options) {
        return ko.observable(options.data.toUpperCase());
      },
    },
  };

  ko.mapping.fromJS(data, personMapping, this);

  this.fullName = ko.computed(function () {
    return this.firstName() + " " + this.lastName();
  }, this);
}

I also create a constructor for my root view model. This way I can also encapsulate the mapping logic and for example create multiple instances of it. Lets imagine we want to have a company view model containing employees. So we get some JSON from the server containing an array of persons called employees and some other properties. The company's name we will convert to uppercase and we will ignore the companies address and website properties.

function CompanyViewModel(data) {
  var companyMapping = {
    ignore: ["address", "website"],
    name: {
      create: function (options) {
        return ko.observable(options.data.toUpperCase());
      },
    },
    employees: {
      key: "peronsId",
      create: function (options) {
        return new PersonViewModel(options.data);
      },
    },
  };
  ko.mapping.fromJS(data, companyMapping, this);
}

Note we create a new person view model for each of the persons in our employees array. The PersonViewModel is responsible for its own mapping (defined in our constructor). We also defined a key for our person object so Knockout knows what makes a person unique. Also note that we didn't customized the way personId is mapped within our person view model.

So everything not specified in the mapping object will by default be wrapped in an observable.

So when we do a request we can instantiate our view model like this.

var comany;
$.ajax({
  url: "http://companyindex.org/getcompanydata/4327/",
  type: "GET",
  dataType: "JSON",
  success: function (result) {
    var data = JSON.parse(result);
    company = new CompanyViewModel(data);
    ko.applyBindings(company, $("#company").get(0));
  },
  error: function (result) {
    //left for brevity
  },
});

We make a call to some url returning the information of a company in JSON format. Create a new CompanyViewModel and apply the bindings. All the mapping logic is in the constructors. See this jsFiddle for a complete example.

Thanks again for reading my article. Please write a comment and share it with your friends and colleagues.

You have disabled cookies. To leave me a comment please allow cookies at functionality level.

More Stories

Cover Image for CI with Jenkins, MSBuild, Nuget and Git part 1

CI with Jenkins, MSBuild, Nuget and Git part 1

MF

Marco Franssen /

Very lately I have worked on setting up some continuous integration (CI) using MSbuild for my c# project. In this blog series I will explain to you how to set up continuous integration. First of all we will start with creating a MSBuild script which will compile our code. We will also create a small batch file for easy executing the MSbuild script. When that is in place, we will add several targets to the build script to run for example unit tests, integration tests, code coverage, packaging e…

Cover Image for Recap my online year 2012

Recap my online year 2012

MF

Marco Franssen /

The year 2012 was for me a year that went way to fast. In the year 2012 I learned a lot new stuff, wrote several blog posts and read lots of blog posts and articles. First of all I want you to give a list of all blog posts I wrote this year. You can find the complete list here http://marcofranssen.nl/2012/ and here http://marcofranssen.nl/2012/page/2/. JavaScript http://marcofranssen.nl/writing-modular-javascript-without-polluting-the-global-namespace/ http://marcofranssen.nl/knockout-js-mapp…

Cover Image for Windows Phone Theme colors

Windows Phone Theme colors

MF

Marco Franssen /

When developing Windows Phone apps I love to use the theme accent colors in my apps. Since there are some new theme colors in Windows Phone 8 I started searching for their color codes. Lucky me I found them on msdn "Theme for Windows Phone"). Now you may be thinking how to use the colors in your own Windows Phone apps. The Color object doesn't contain a color called Emerald. So I created a small class to help me out with this issue. First of all I created a small static helper method to convert…

Cover Image for Unblock downloaded files with PowerShell

Unblock downloaded files with PowerShell

MF

Marco Franssen /

Have you ever got in the situation that you downloaded a zip-file and figured out to late the files are blocked? So you extracted the zip file into a folder with existing items. It will be damn hard to figure out which files needs to be unblocked. Besides that it will cost you many work to do so by right-clicking all of the files and clicking the unblock button. Unblock file Luckily we have PowerShell and we can easily write a little script to execute the unblock operation on the files in a sp…