Making a REAL web application with ASP.NET Web API Part 3, the JavaScript and markup
8/2/2012Ok, so a little late, but there is a lot of code to cover for this section. Not terribly complicated, just a lot of groundwork to make life easier as you try to scale the app up later. So for part 3 of Making a real web application using blah blah blah.. we will be talking a lot about javascript/jQuery, a little tutorial on handlebars.js (with a link to go to the source of the info!), and probably some other stuff too. If you have been following along with the last two posts, then this should be the fun part for you, where we start to bring it all together.
Warning, I should probably split this into 2 or 3 posts, but since all the code ties together, I would rather have it all in one place. That being said, grab your cup of coffee and lets get to it.
The end goal
So you can try to see what this will look like, here are some captures of the application. I will try to get a live demo up soon for the public. I don't want to put my current one up since I really haven't implemented what would need to be in place for that to be a smart move.
This one is the list view of all the task we current have.
Here is the view where we can add a new task, or modify an existing one.
And finally, the detailed view of the task.
The styling I chose to emulate was Metro. Love it, and can't get enough of it.
A Server interface
Everything we are going to be focusing on is how to interact with this data on the client side, but first we need to establish how we are going to get the data in the first place. The following is simple javascript that uses what is called a revealing module pattern. Here is the whole code for the server interface:
var onusServer = (function () { var onusApiUrl = '@Url.RouteUrl("DefaultApi", new { httproute="", controller = "Onus"})'; $(document).ajaxError(function (event, xhr) { alert(xhr.status + ":" + xhr.statusText); }); var getOnuses = function () { return $.ajax(onusApiUrl); }; var getOnus = function (id) { return $.ajax(onusApiUrl + "/" + id) }; var addOnus = function (onus) { return $.ajax(onusApiUrl, { type: "POST", data: onus }); }; var updateOnus = function (onus) { return $.ajax(onusApiUrl + "/" + onus.Id, { type: "PUT", data: onus }); }; var deleteOnus = function (id) { return $.ajax(onusApiUrl + "/" + id, { type: "DELETE" }); }; return { getOnuses: getOnuses, getOnus: getOnus, addOnus: addOnus, updateOnus: updateOnus, deleteOnus: deleteOnus }; }());
Really nothing terribly involved here. First I user a Razor Helper to get the url to my api and throw that into the onusApiUrl. Then it is just a matter of defining a function for each http verb that you need and returning an ajax result from those functions. Finally at the end of the module we return public Methods that will allow us to make calls like 'onusServer.getOnus(id)' from the rest of our code. Also you will notice at the top the quick and dirty error handler. This just pops up an alert to show what went wrong with your ajax request.
Handlebars.js Templates
These templates are awesome. I am new to discovering any kind of templating with javascript, so forgive me if I sound ignorant on some matters. Handlebars is an extention of mustache.js (get it?) and packs in some pretty cool features. In my examples I don't get into anything very complicated, but if you go on over to their site you can see just how much there is inside this small little library.
I am going to have to put these here as images since I haven't gotten around to figuring out why ckEditor throws up every time I put some markup in here.
So I have a basic unordered list that I will be using to cram some data into. The handlebars syntax used double curly braces to indicate where you are templating. The first one you see is {{#each onus}}. This starts a for each loop. So for every onus that I have passed in, grab the values I call below ({{Title}}, {{Status}}, {{Description}}). You will see later that in our javascript to render these we pass in an object called onus, and the properties we call match the names of our properties in our class we made back in Part One.

Doing more or less the same thing here, just grabbing more data. One new thing you see here is the {{#if DueDate}} section. Since this is an optional field, if the object being passed in does not have a value for DueDate the template will not render that section. Very handy.
Here is the same approach. One thing to note is I have an id property on each input, this is so I can later grab them easily using jQuery to POST or PUT them to my api. One bug I have not worked out yet is getting the date and the status to default to whatever the incoming data is. But otherwise, it works like a charm.
The javascript that makes the magic
This is another Module pattern I will use here. Not the revealing kind though. First I want to show you how this gets started. We create a function that executes with the DOM is ready, and call all the things that need calling for it to work.
$(function () {
compileTemplates();
wireEvents();
refreshOnuses();
});
So we have three basic functions that we need to code up:
- template compiling
- event wiring
- refreshing the data
Template Compilation:
var templates = {}; var compileTemplates = function () { templates.onusList = Handlebars.compile($("#onusList").html()); templates.onusDetails = Handlebars.compile($("#onusDetails").html()); templates.onusCreate = Handlebars.compile($("#onusCreate").html()); };
Compilation is fairly simple with handlebars. First I create an empty object called templates. Then I add each template by grabbing the id of the script tag that the template live in. So now the compiled templates live in templates.* where I can call on then later to render onto my page.
Event Wiring:
var wireEvents = function () { $(document).on("click", ".oneOnus", viewOnus); $(document).on("click", "#saveOnus", saveOnus); $(document).on("click", ".editOnus", viewOnusEdit); $(document).on("click", "#createOnus", viewOnusCreate); $(document).on("click", ".deleteOnus", deleteOnus); };
We have a few basic event we need wired up after the DOM gets loading. As you can see from the class/id names I am calling these event will handle my GET(1), POST, PUT, DELETE, and rendering some hidden template html to the page. We have definitions for what method to call in each of these, which we will walk through shortly.
Refreshing the data
var refreshOnuses = function () { onusServer.getOnuses().done(showAllOnuses); }; var showAllOnuses = function (data) { var output = templates.onusList({ onus: data }); $("#onusListOutput").html(output); };
Here we see two functions that are tied together. The refresh function simple grabs all the data for the onusServer.getOnuses() and when ajax says that it is done (.done()) we call showAllOnuses. In there we simple grab the template we want, give it the data object, and put the compiled html template output into a div on our page. I will go more in details into this shortly.
So that is the gist of what the rest of our javascript is going to do. I will walk you through the functions we have wired to our click events, and how you output our html to the screen using the handlebars templates next.
The javascript that is the magic
First lets look at how we can see the details on a single one.
//Details var viewOnus = function () { var id = getId(this); onusServer.getOnus(id).done(viewOnusDetails); }; var viewOnusDetails = function (onus) { var output = templates.onusDetails(onus); $("#onusDetailsOutput").html(output); };
So we have that click event we wired up to each onus in the list to call viewOnus(). In there we call a function called getId() and pass in the current context. I will show you getId() in a minute. Once we have that id we call into our server interface to the getOnus(id) method to return us a single object. When ajax lets us know that is done we jump into viewOnusDetails where we grab the template, hand it the data, and throw the html inside our empty div on the page. Here is getId as well as two other 'Helper' functions you will see shortly.
var getId = function (element) { return $(element).parents("li").attr("data-id"); }; var clearCreate = function () { $("#onusCreateOutput").empty(); }; var clearDetails = function () { $("#onusDetailsOutput").empty(); };
As you can see, getId simple looks up the DOM tree from where it was called (the element that was clicked) and finds the li parent and grabs the value from the data-id attribute. Look at the template code and you will see we have based in the onus id into there. We also have two clear methods that will help remove the Details and Modify html once I am done with it, so on a Save or Delete.
Creating a new onus is pretty simple
var viewOnusCreate = function (onus) { var output = templates.onusCreate(onus) $("#onusCreateOutput").html(output) };
Since we are making a new one, all this one does is bring up an empty form.
Before we look at how we save and post it, we need to see how we bring up the same edit form with an existing onus to modify it.
var viewOnusEdit = function (onus) { var id = $("#detailsId").val() var data = onusServer.getOnus(id).done(fillForm); }; var fillForm = function (data) { var onus = data; var output = templates.onusCreate(onus) $("#onusCreateOutput").html(output) };
So here we get the Id just using a jQuery selector. Then we call the server interface to find that object based off of the id. When it is done we call fillForm. In there we map the data to a variable called onus, grab the html template, and spit it out on the screen with our data passed into it.
Now we get to the fun part of saving this data.
//Save/PUT/POST var saveOnus = function () { var onus = { Id: $("#id").val(), Title: $("#title").val(), Description: $("#description").val(), Details: $("#details").val(), DueDate: $("#dueDate").val(), Status: $("#status").val() }; var operation; var id = $("#id").val(); if (id == 0) { operation = onusServer.addOnus(onus); } else { operation = onusServer.updateOnus(onus); } operation.done(refreshOnuses, clearCreate, clearDetails); return false; };
Here we are grabbing all the values from the form and putting them in an object called onus. At this point we need to detect if it is new or existing. We do this by checking the Id. If it is 0, which i default to for new ones, we do the POST, otherwise we can safely assume it is existing and do an update.
Last part is DELETE
var deleteOnus = function () { var id = $("#detailsId").val(); clearDetails(); onusServer.deleteOnus(id).done(refreshOnuses); };
That is it for delete. Simple grab the id we are deleting, remove the details template from being viewable, delete the object, then refresh your list.
Conclusion disclaimers & shout outs
If you want all this code you can shoot me an email and I would be glad to share, or wait for me to upload it to the site for download. I believe I mentioned this in the first post but it is worth mentioning again. I learned about this design pattern and the different technologies for a course I watched on pluralsight that was authored by the code-genius Scott Allen. I expanded and modified things here to fit my needs, and will use it as a good framework to creat a pretty robust app, but I alway like to give credit where credit is due. That note being said, the code above is mine, no copypasta here, so I promise you there will be bugs and possibly missing functionality. Deal with it.
My next step here is to figure out how to incorporate users into a system like this. Once that is panned out I will probably put something out live where you can play around with it, give feedback, and even use it for a productivity tool!
If you made it this far, I thank you for reading.
Regards
{David Stanley}