DotNetCurry Logo

Getting Started with KnockoutJS in ASP.NET MVC

Posted by: Suprotim Agarwal , on 8/22/2013, in Category ASP.NET MVC
Views: 144934
Abstract: In this Knockoutjs + ASP.NET MVC post we take a look at the slight mind shift that ASP.NET MVC developers working on JavaScript and jQuery face when they first start using KnockoutJS. We use the new and shiny Visual Studio 2013 Preview with it’s glorious code highlighting and formatting support for Knockout Syntax

Knockout JS is a fantastic library when you are looking for a drop-in enhancement that brings in client side data-binding and elements of the MVVM design pattern into your website that’s a potpourri of Razor syntax, server side Html Helpers and jQuery plugins.

Well Knockout JS was built to bring meaning into this chaos, but before you get started, you have to detune yourself a bit. Let’s see what that means. By the way, this article assumes you have the KO cheat-sheet created by Sumit Maitra handy so that you can quickly lookup some of the syntax we throw around. Sumit has helped me a great deal in co-authoring this article and just like an AngularJS +  MVC series, we are planning to collaborate to do a KnockoutJS series too.

Read the second part of this article - KnockoutJS and ASP.NET MVC – Alternate techniques and using the Anti Forgery Token

 

Separating the Server side and the client side

Most newbies have trouble visualizing the server side and the client side, especially devs coming from WinForms background. ViewBag is often interpreted as ViewState and then all hell breaks loose. Here is the thumb rule:

Rendered on Client (i.e. the browser) == HTML only (plus JavaScript of course)

Rendered on Server == cshtml markup + all the Razor expressions a.k.a. anything that starts with an @.

So anything that was written in the CSHTML file including binding of a Select to a SelectList in ViewBag, is all interpreted by the “View Engine” and converted into HTML BEFORE it reaches the browser.

This distinction is extremely important to understand. Once this distinction is clear, you know exactly what you have in the DOM when you are writing the $(document).ready (…)

ViewModel is the starting point of your UI

Knockout works with a ViewModel that can either be a JS object or a JS function. Either ways, your ViewModel is your data source. Always think of what you need to do to update the ViewModel in order to get a certain output on the view and not how to update the view by hacking the DOM using jQuery or JavaScript.

Let’s take an example. Say you have a C# object - Lookup

public class Lookup
{
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}

Consider the requirement where you have to render all the existing lookups and add new ones. You can easily render a collection into an HTML <table>. However you are spoilt for choices when it comes to adding a new Lookup value. You could use a jQuery UI plugin or Bootstrap plugin to show a fancy modal dialog that would temporarily store the data for new item. If you are hitting the server as the dialog is dismissed you are good. What if you have to avoid hitting the server on dialog dismissal, but maintain the changes till an Explicit save function is called? You potentially have to keep track of changes in more than one Lookup and looping the DOM elements on click of the Save button isn’t the best way to ‘get’ the latest data. Another requirement could be you want to add new rows inline in the table itself and Edit it inline as well. How could we do that without hacking the DOM?

Observables in Knockout help you with ‘Two-Way’ Data Binding

Observables in Knockout are built for the precise purpose of keeping track of changes and updating the UI as changes happen in the ViewModel. So if you consider converting your Server Side Data into a JavaScript object collection on the client side and add, delete or modify values in this collection, you can post the updated collection back once you are ready with all your changes in one go. Best part is if you bind your <table> to the JavaScript object collection, you don’t have to write any DOM manipulation code at all.

Example

Consider the above Lookup class and assume we have scaffolded a Controller and CRUD operations + UI for it in an ASP.NET MVC Application Template. Our UI looks like this for the Index

non-ko-index

The Server side markup for this is as follows

@model IEnumerable<KnockoutSamples.Models.Lookup>
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
  <th>
   @Html.DisplayNameFor(model => model.Key)
  </th>
  <th>
   @Html.DisplayNameFor(model => model.Value)
  </th>
  <th></th>
</tr>
@foreach (var item in Model)
{
  <tr>
   <td>
    @Html.DisplayFor(modelItem => item.Key)
   </td>
   <td>
    @Html.DisplayFor(modelItem => item.Value)
   </td>
   <td>
    @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
    @Html.ActionLink("Details", "Details", new { id = item.Id }) |
    @Html.ActionLink("Delete", "Delete", new { id = item.Id })
   </td>
  </tr>
}
</table>

When we click on Create, the server routes us to a different page and when we save on that page, we get routed back to the Index page.

non-ko-create

Business requirements never match scaffolded/auto-generated templates, because if they did, we would have automated software development a long time ago. So let’s say our requirement changed and now we have to do the Create, Update and Delete actions inline, in the Index page.

This would present to us the perfect opportunity to introduce a ViewModel and start using KO to data-bind the UI to the view Model. We add KnockoutJS to our Project and add a knockout.samples.js for our custom scripts.

The script in the knockout.samples.js file is as follows to start off with. The viewModel object has only one property the lookupCollection of type ko.observableArray(). This means anytime an item is added or removed from the Array, Knockout will update any UI elements that are bound to this collection.

viewModel = {
lookupCollection : ko.observableArray()
};

$(document).ready(function () {
$.ajax({
  type: "GET",
  url: "/Lookups/GetIndex",
}).done(function (data) {
  $(data).each(function (index, element) {
  viewModel.lookupCollection.push(element);
});
  ko.applyBindings(viewModel);
}).error(function (ex) {
  alert("Error");
});
});

Next we have the jQuery document ready function. Here we directly do an AJAX get to retrieve some data from the server. Once the server returns data (essentially the list of Lookup objects as JSON), we populate the lookupCollection in our viewModel. As we are pushing data into the observable collection, KO is updating the UI for us automatically.

One small gotcha remains, the URL that our AJAX call is pointing to - GetIndex action method, doesn’t exist yet, lets put that in:

public ActionResult Index()
{
    return View();
}
public JsonResult GetIndex()
{
    return Json(db.Lookups.ToList(), JsonRequestBehavior.AllowGet);
}

Actually we’ve got two changes here. The Index method now returns an empty View and the GetIndex method returns a Json array of the Lookup Objects.

Then we update the Index.cshtml and get rid of all the server side ‘stuff’

<title>Index</title>
<h2>Index</h2>
<p>
<button class="btn btn-primary" id="create">Create</button>
</p>
<table class="table">
<tr>
  <th>
   Key
  </th>
  <th>
   Value
  </th>
  <th></th>
</tr>
<tbody data-bind="foreach: lookupCollection">
  <tr>
   <td data-bind="text: Key"></td>
   <td data-bind="text: Value"></td>
   <td>
    <button class="btn btn-success">Edit</button>
    <button class="btn btn-danger">Delete</button>
   </td>
  </tr>
</tbody>
</table>

Look Ma, no Razor syntax! Okay I cheated we still have @section for Scripts, but apart from that there is no Razor syntax.

Understanding the KO DataBinding

As you can see in the markup above, there are some special data-bind attributes. These are HTML5 compliant attributes that Knockout uses to bind the viewModel to the HTML View.

The foreach binding - The <tbody> is bound using what’s referred to as foreach binding. This tells KO that it should bind to an array by the name lookupCollection. KO automatically assumes the content inside the <tbody> should be repeated for every item in the lookupCollection.

The text binding - Inside the foreach, we have an entire <tr> defined. This will be repeated for as many items that are there in the collection. Now the binding context automatically changes from the collection to the item in the collection. So when we databind to text: Key it tell KO to pull out the Key property of the current item and in this case (since it’s bound to the td) place it as inner text.

Nice! Let’s run the application and visit the Lookups’ Index page

ko-index-page-initial

Wow, super cool! But now our Create/Edit/Delete buttons no longer work! What now?

Editing objects on Client Side

Since the aim of this article was to showcase how to use ViewModel as opposed to DOM manipulation, we’ll apply some more KO features now.

Creating different templates for Edit and View

In the above markup for Index.cshtml, we have assumed that the view is going to be read-only, but what if we want to toggle between a readonly view and Edit view when user clicks on the Edit button? KO has a solution for that – Named Templates.

Named Templates are a KO feature that allows you to define snippets of markup tagged with a name to identify it. KO then replaces this snippet wherever the template binding calls requests for it.

For example: in our above sample, we have the <tbody> bound to the lookupCollection. The <tr> contains the <td> elements that are bound to the Key and Value properties of the viewModel. However we want two different modes now, display and edit. In display mode, the view will be as it is currently, i.e. use Labels, but in edit mode we should have Input boxes that allow us to save the changes. Solution? Define two templates one for display and one for edit.

The Updated markup for the <tbody> is as follows:

<tbody data-bind="foreach: lookupCollection">
<tr data-bind="template: { name: Mode, data: $data }">
</tr>
</tbody
>

As you can see here, the <tr> is now bound to a template whose name is defined in a property called Mode and whose data comes from a variable $data.

Hold on to the thought that we have a new property in our Lookup object called Mode, we’ll see ‘how’ in a minute.

The $data variable is provided by KO and it is a pointer to the current viewModel element that is available for data binding. So in this case, since we are inside the lookupCollection, $data represents a Lookup element from the collection. Thus we are passing the current element to the Template and elements inside the template can bind to the properties it.

The Templates are defined as follows:

<script type="text/html" id="display">
    <td data-bind="text: Key"></td>
    <td data-bind="text: Value"></td>
    <td>
        <button class="btn btn-success kout-edit">Edit</button>
        <button class="btn btn-danger kout-delete">Delete</button>
    </td>
</script>

<script type="text/html" id="edit">
    <td><input type="text" data-bind="value: Key" /></td>
    <td><input type="text" data-bind="value: Value" /></td>
    <td>
        <button class="btn btn-success kout-save">Update</button>
        <button class="btn btn-danger kout-cancel">Cancel</button>
    </td>
</script>

So templates are defined as Script blocks to keep them valid HTML and given a unique ID. This is the name that we saw earlier in the template binding. So when our Lookup Element’s Mode property is set to display, the first template is used (readonly). When it is set to edit, the second template (using input boxes) is shown.

Also note the Buttons change Text in Edit Mode.

Last but not least we have added classes starting with kout- to our Edit, Delete, Update and Cancel buttons. Why?

This is because we need click handlers for the buttons and since these buttons are part of a template we cannot give them an Id in the markup because the ID would get duplicated. So to attach click handlers we use the class name.

Applying Templates and Switching them on-the-fly

Now that we have added the templates, time to apply them. The key thing missing is the Mode property that’s not available on the Server side, so it won’t be present in the JSON that come back from the server. Solution? We create our custom object and add the custom object to the collection. Sounds complicated but it isn’t, here is what the done function looks like

.done(function (data) {
$(data).each(function (index, element) {
  var mappedItem =
  {
   Id: ko.observable(element.Id),
   Key: ko.observable(element.Key),
   Value: ko.observable(element.Value),
   Mode: ko.observable("display")
  };
  viewModel.lookupCollection.push(mappedItem);
});
ko.applyBindings(viewModel);
})

As we can see, we were earlier looping through the incoming array and directly pushing the JS object into the lookupCollection. Now, we are creating a new object called mappedItem with four properties, Id, Key, Value and Mode. Key and Value is set to the values we get from the server, whereas the Mode property is set to “display” by default because initially everything will be in readonly mode.

Switching Modes

We need to switch modes when user clicks on Edit and Update. So to do that we bind the ‘click’ event handlers using the jQuery $(document).on(…) function

$(document).on("click", ".kout-edit", null, function (ev) {
var current = ko.dataFor(this);
current.Mode("edit");
})

$(document).on("click", ".kout-update", null, function (ev) {
var current = ko.dataFor(this);
current.Mode("display");
})

Here we see another KO function in use dataFor. This function retrieves any KO object that is bound to the current context. Since we clicked on the Edit/Update button of a row that’s bound to a mappedItem object, KO will return it. Once we have the currently object, we simply flip the Modes.

Running the Application, we can see we can toggle between edit and display modes.

ko-index-page-edit-mode

ko-temp-updated

Nice! So without manipulating DOM manually, we have managed to implement seamless Edit functionality. Though data is not going to the Server yet. Let see how we can add the Create functionality as well and then we’ll send data to the server too.

Creating new Rows of Data and Updating Modified Data

We hookup the Create button a click even handler and create a new JS object with the properties Id, Key, Value all set to empty or (zero) and the Mode set to edit. Then we push it into the viewModel.lookupCollection and voila we are done! No change in markup required.

ko-create-new

Yup, to get the above all we need is the following snippet of code added to our knockout.samples.js file

$(document).on("click", "#create", null, function (ev) {
var current = {
  Id: ko.observable(0),
  Key: ko.observable(),
  Value: ko.observable(),
  Mode: ko.observable("edit")
}
viewModel.lookupCollection.push(current);
});

Next we add a saveData function that takes in a currentData object which is the current LookupItem being edited/added as input parameter.

The function checks if the Id property exists and if it’s non-zero positive number (indicating it has already been saved earlier). If it has a non-zero ID, the current data is POSTed to the /Lookups/Update action. If the ID is 0 the new data is POSTed to the /Lookups/Create action method

function saveData(currentData) {
    var postUrl = "";
    var submitData = {
        Id : currentData.Id(),
        Key : currentData.Key(),
        Value: currentData.Value()

    };
    if (currentData.Id && currentData.Id > 0) {
        postUrl = "/Lookups/Edit"
    }
    else {
        postUrl = "/Lookups/Create"
    }
    $.ajax({
        type: "POST",
        contentType: "application/json",
        url: postUrl,
        data: JSON.stringify(submitData)
    }).done(function (id) {
        currentData.Id(id);
    }).error(function (ex) {
        alert("ERROR Saving");
    })
}

We need to make small changes on the Server side. For one, we’ll temporarily disable AntiForgeryToken and next we’ll return a Json with the Id of the entity create/edited. So the update Create method is as follows:

[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Create(Lookup lookup)
{
    if (ModelState.IsValid)
    {
        db.Lookups.Add(lookup);
        db.SaveChanges();
        return Json(lookup.Id);
    }

    return View(lookup);
}

The Updated Edit method is as follows

[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Edit(Lookup lookup)
{
    if (ModelState.IsValid)
    {
        db.Entry(lookup).State = EntityState.Modified;
        db.SaveChanges();
        return Json(lookup.Id);
    }
    return View(lookup);
}

Now if we run the application and click on “Create”, we’ll see a new blank row is added:

ko-create-new

When we hit the Update button, the data gets Saved.

ko-new-created

Next we click Edit and the mode changes to Edit Mode.

ko-edit-client

 

We update the value and hit Update to save the changes.

On the server we see the following:

ko-edit-server

Isn’t that awesome.

We managed Create/Update and List functionalities without manipulating DOM elements manually. No $(<selector>).val() called. No HTML manipulated or injected in our code anywhere!

That’s the magic of KO and working with the View Model.

Based on the comments received in this article, read the second part of this article - KnockoutJS and ASP.NET MVC – Alternate techniques and using the Anti Forgery Token which looks back at the suggestions we received in the comments of that article and answers the questions and explores alternate techniques of doing things

Conclusion

We conclude this post here. This series was inspired by Sumit’s experiences with some new members of a team he is consulting. Learning something new is all about getting in the right groove. Hopefully this article and possibly future ones will get you in the KO groove. Feel free to share your comments/experiences, tips and tricks while learning KO and we’ll try to incorporate them in the future.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and a new one recently at The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for nine times in a row now. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that represents premium web sites and digital publications comprising of Professional web, windows, mobile and cloud developers, technical managers, and architects.

Get in touch with him on Twitter @suprotimagarwal, LinkedIn or befriend him on Facebook



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Abu Hamzah on Thursday, August 22, 2013 9:13 PM
Do i need to wire-up delete/cancel events?, clicked on Delete/Cancel nothing fires-up
Comment posted by Sumit on Thursday, August 22, 2013 10:19 PM
Hello Abu,
Yes, the Sample doesn't include the implementation for Delete. Wiring up Delete is straightforward. Do a POST to the DELETE action link with the Id.

Cancel is trickier, 'cause it needs to revert changes if any. Checkout the Protected Observable sample here http://www.dotnetcurry.com/ShowArticle.aspx?ID=876 or hang in there for the followup :-).

Cheers,
Sumit.
Comment posted by Simon on Friday, August 23, 2013 6:41 AM
Hi,

I always struggle to get my head around the fact we make a request to get the base html for the page and once that has rendered go off and make another call to the server to get the data.

Would it not be better to serialise our model returned on the first load and pass to knockout that way meaning we have done so in one trip?

Cheers
Comment posted by Karthic Raghupathi on Saturday, August 24, 2013 10:00 PM
I belive there is a typo in the code where you specify the template as follows:
<tr data-bind="template: { name: Mode, data: $data }">

I was getting this error: 'Unknown template type: [object Window]'

Upon doing some googling I stumbled upon the answer here: https://groups.google.com/forum/#!topic/knockoutjs/rddBbNdgS8o

I changed the code as follows to get it to work:
<tr data-bind="template: { name: Mode(), data: $data }">
Comment posted by Troels on Sunday, August 25, 2013 4:24 AM
Overall a good article, but i don't understand why:

1. For create you use the .on() function when a simple $('#create').click() is enough, but that is a very minor thing

2. Why you use jquery for the click handling in the first place instead of simply showing off the KO click binding, which is both cleaner and makes you able to see what happens just by looking at the markup. I think showing off bad practices in a tutorial like this is unfortunate as you risk people start using it wrongly.
Comment posted by Simon on Monday, August 26, 2013 3:02 AM
Good article! But it is not recommended to use document as event handler. In this case, it is fairly enough to use '#tbody' as selector.
Comment posted by Simon on Monday, August 26, 2013 3:04 AM
Last comment was ment for update and edit buttons, Troels already mentioned selector for create button.
Comment posted by Troels on Monday, August 26, 2013 2:21 PM
@Simon

Indeed, i thought about the use of document as well, but didn't mention it because i think the real problem for the edit/update buttons was the awkward use of classes just to make it work with jQuery instead of simply using the KO click binding which would solve all that without complicating the tutorial.
Comment posted by Seamless on Monday, August 26, 2013 3:35 PM
Could you explain why [ValidateAntiForgeryToken] needs to be commented out?
Comment posted by Mark on Tuesday, August 27, 2013 2:23 AM
Hi - I'm interested in the temporary disabling of the antiforgery token too.  I doesn't appear to be reinstated in the code anywhere.  Is this a necessity for using Ajax (and therefore you have to accept you have a less secure application)?  Thank you, Mark
Comment posted by Suprotim Agarwal on Tuesday, August 27, 2013 5:20 AM
@Karthic Good catch, actually the fix is in the ViewModel. The Mode property should be an observable. It is in our code download, we'll fix the markup here shortly.
Comment posted by Sumit on Tuesday, August 27, 2013 8:31 AM
Thanks everyone for your inputs, stepping in here as I has some hand in the KO code.


1. Simon Aug 23rd - Good point, we'll follow up with how to do that.

2. Troels Aug 25th - Kind of agree on the ('#create').click(). On your second point, I have found it easier to work with jQuery event handlers than KO Functions. But if you can point me to recommendations/best practices as to why one is better than the other I will gladly propose one over the other. To keep users' options open, in the next sample we will demo the KO click binding to give users both the options.


3. Simon (Aug 26) - Thanks again, agreed reducing the scope to #tbody is better, I've to stop being lazy about using a the $(document) bazooka.


4. Troels (Aug 26) - ditto as my point 3. above


5. Seamless and Mark : We commented out [ValidateAntiForgeryToken] just for this demo. We were overzealous about taking out server side code. When we removed @Html.AntiForgeryToken() from the cshtml, we no longer had a AntiForgeryToken with us to send back to the server. That's why we had to comment out the attribute on the server else MVC will throw AntiForgeryToken not found. We'll followup with how to keep AntiForgeryToken and still do AJAX post.
Comment posted by john on Wednesday, August 28, 2013 1:04 PM
Hi,
I'm trying to run the solution but give a error!
not load the corret page appear the asp.net page welcome.
I see that error is:

"'iexplore.exe' (Script): Loaded 'Script Code (Windows Internet Explorer)'.
Exception was thrown at line 5288, column 6 in http://localhost:4297/Scripts/jquery-1.8.2.js
0x800a139e - JavaScript runtime error: SyntaxError"

Any solution?

Thanks
JL
Comment posted by Sumit on Thursday, September 5, 2013 6:29 AM
Hi John,
Can you share the Browser version (IE version) and also if you were using the Project Directly downloaded from Github or you followed the article to setup the code yourself.
The only JavaScript errors I am seeing are 'Artery' related.

Thanks,
Sumit.
Comment posted by ankush jain on Friday, November 8, 2013 1:11 AM
change Mode to Mode()
at
<tr data-bind="template: { name: Mode, data: $data }">

It has wasted my  1 hour.
Comment posted by Adrian James Carney on Wednesday, January 22, 2014 5:16 AM
Hi,
This is a really interesting tutorial. But I gave one question.
I am quiet new to mvc and completely new to knockout.js. I am trying to implement an application that sends data back to two different models instead of one. How would I go about modifying your solution to do this?
Thank you
Comment posted by Adrian James Carney on Wednesday, January 22, 2014 5:47 AM
Hi,
This is a really interesting tutorial. But I gave one question.
I am quiet new to mvc and completely new to knockout.js. I am trying to implement an application that sends data back to two different models instead of one. How would I go about modifying your solution to do this?
Thank you
Comment posted by Adrian James Carney on Wednesday, January 22, 2014 6:19 AM
Hi,
This is a really interesting tutorial. But I gave one question.
I am quiet new to mvc and completely new to knockout.js. I am trying to implement an application that sends data back to two different models instead of one. How would I go about modifying your solution to do this?
Thank you
Comment posted by Adrian James Carney on Wednesday, January 22, 2014 9:27 AM
Hi,
This is a really interesting tutorial. But I gave one question.
I am quiet new to mvc and completely new to knockout.js. I am trying to implement an application that sends data back to two different models instead of one. How would I go about modifying your solution to do this?
Thank you
Comment posted by dwd on Wednesday, February 25, 2015 12:45 AM
wdwdqw