In our Getting Started with KnockoutJS in ASP.NET MVC article, we started off with the intention of weaning web client devs away from hacking up the DOM and using client side ViewModels with Knockout as much as possible. We got some good questions regarding the article of which the main ones are:
1. Why do we have to get the empty View using the Get Action method and then do a second HTTP Get using AJAX after Document load to get the actual data? We’ll see one of the ways to avoid the second Get.
2. Why are we using jQuery’s $(element).on(…) to assign click handlers instead of having the click handler functions in the ViewModel itself. Well we can, so we’ll see how to do so.
3. Why did we disable AntiForgeryToken? Do we really need to make our code ‘insecure’ to use JavaScript based clients? Nope, we don’t have to make our code ‘insecure’, we’ll see how we can put the AntiForgeryToken back and use it to validate input.
Finally we’ll see how we can avoid manual mapping between the Server side ViewModel and Client Side ViewModel.
We start off with the code from the first article so you can download the code or fork the code on Github. This article has been written by Sumit Maitra and I.
Avoid doing a second AJAX GET to get Data as JSON
On way to send data to Client as JSON from the server is to serialize the Data as a JSON string, stick it in a ViewModel property and bind it to an HTML element (like a Hidden Field) or use Razor syntax to include the data in a Hidden field directly.
We’ll use the second technique today.
Changes in LookupsController
We update the Index action method to pass the list of Lookups into the ViewResult.
public ActionResult Index()
{
return View(db.Lookups.ToList());
}
Just to ensure that we are not calling old code by mistake, we’ll comment out the GetIndex Action method too.
Updating the Index View
We put back the Model declaration on top of the View and add the Hidden Input variable that will save the JSON serialized Model.
@model IEnumerable<KnockoutSamples.Models.Lookup>
<title>Index</title>
<h2>Index</h2>
<input type="hidden" id="serverJSON"
value="@Newtonsoft.Json.JsonConvert.SerializeObject(Model)" />
<!-- rest of the markup -->
If you went through the first article carefully, you’ll realize this works because all Razor syntax is evaluated on the server when generating final HTML markup that’s sent back to the Browser. So here the @Newtonsoft.Json.JsonConvert.SerializeObject will be evaluated on the server for the data that we passed into the View(…) function in the Index Action method. So value of the hidden field will essentially end up with a long JSON string.
Updating the JavaScript to use the Hidden Field as the Data Source
We update the $(document).ready function to retrieve the data object from the Hidden Field instead of a AJAX call to the server. I’ve kept the code that we remove as commented and highlighted in gray. The code to convert the JSON string to JS object is highlighted in Yellow. Note the rest of the code remains the same.
$(document).ready(function () {
//$.ajax({
// type: "GET",
// url: "/Lookups/GetIndex",
//}).done(function (data) {
var data = JSON.parse($("#serverJSON").val());
$(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);
//}).error(function (ex) {
// alert("Error");
//});
//… Rest of the code
}
With that we are all done, if we run the application now, we’ll see the three default items come up in the Index.
If you run F12 dev tools, you’ll see that there is only one AJAX Get that’s made by Visual Studio’s browser link tech by the injected SignalR code.
Handling Edit, Update, Delete in ViewModel functions rather than external Event handlers
There were some protestations on why were we using jQuery ‘On’ function to attach Click event handlers for Edit, Delete etc. and it was suggested that we add the functions to the View Model and be done with it.
To Use KO View Model functions, we need to declare two more functions in the ViewModel. As you can see below, we’ve added two functions Edit and Update. KO automatically passes the data in current context, so we don’t have to extract it using ko.dataFor. The rest of the implementation is copied over from the older event handler.
$(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"),
Edit: function (current) {
//var current = ko.dataFor(this);
current.Mode("edit");
},
Update: function (current) {
//var current = ko.dataFor(this);
saveData(current);
current.Mode("display");
}
};
viewModel.lookupCollection.push(mappedItem);
});
Binding the Button Click handlers
We now have functions to handle the events but we need to assign them to the respective button’s click events. Since the new methods are a part of the View Model, we’ll use KO’s click binding in the markup.
The Edit Button:
<button class="btn btn-success kout-edit" data-bind="click: Edit">Edit</button>
The Update Button:
<button class="btn btn-success kout-update" data-bind="click: Update">Update</button>
If you run the app now, things will work as before. Pretty neat and doesn’t seem to be a troublemaker, so let’s keep it as is and go ahead!
Getting the Anti Forgery token back
We have covered what Anti Forgery tokens are and how they help prevent CSRF attacks here. We also saw how to use it in Web API services. So lets see how we can continue using it in our KO based app.
1. First we restore the AntiForgeryToken Razor syntax in the Index.cshtml. We add the following at the top of the page just below the @model declaration.
@Html.AntiForgeryToken()
This simply adds a Hidden field to the final HTML page.
2. Next we enable the Token Verification on the Controller method, so we uncomment the highlighted line below in the LookupsController
[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);
}
3. If we try submitting now, we’ll get the AntiForgeryToken validation failed error. So finally, we update the saveData function in the knockout.samples.js. We make three simple changes:
a. Add the __RequestVerificationToken to the JS object that we are submitting on Save. The property name is so because that’s what MVC looks for in the submitted form and that also happens to be the name of the hidden field in which it is stored.
b. Change contentType to from-url-encoded
c. And finally since we are sending a url-encoded form, so we no longer JSON.stringify submitData and send the JavaScript object instead
function saveData(currentData) {
var postUrl = "";
var submitData = {
Id: currentData.Id(),
Key: currentData.Key(),
Value: currentData.Value(),
__RequestVerificationToken: $("input[name='__RequestVerificationToken']").val()
};
if (currentData.Id && currentData.Id() > 0) {
postUrl = "/Lookups/Edit";
}
else {
postUrl = "/Lookups/Create";
}
$.ajax({
type: "POST",
contentType: "application/x-www-form-urlencoded",
url: postUrl,
data: submitData
}).done(function (id) {
currentData.Id(id);
}).error(function (ex) {
alert("ERROR Saving");
})
}
Okay, that’s three different questions solved and a different perspective towards using KO achieved.
Conclusion
Today we revisited some of the questions that rose in our previous article and explored some alternate techniques when using Knockout JS and ASP.NET MVC. We also saw how we could secure our application using the AntiForgery Token that we had ignored previously. In the next part of the series, we’ll step it up and learn more techniques using Knockout JS in near production scenarios.
Download the entire source code of this article (Github)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
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
The Absolutely Awesome jQuery CookBook.
Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.
Get in touch with him on Twitter @suprotimagarwal or at LinkedIn