Javascript promises and why jQuery implementation is broken

Standard

Introduction to Javascript promises

Callbacks: a classic approach to async

Callbacks are Javascript classic approach to collaborative asynchronous programming.
A callback is a function object that is passed to another function as a parameter and that  later on must be invoked under some circumstances: for example when an asynchronous function successfully completes a task, it invokes the callback function to give back control to the function that was previously executing, signaling that the task has completed.
Callbacks are easy to use, but they make the code less readable and messier, especially if you have few of them one after another using anonymous functions:

Small example

function invokingFunction() {
    // some stuff
    asyncFunction(function(data) { // the first callback function
                   anotherAsyncFunction(function() { // the second callback function
                          //more stuff
                   });
             });
}

This pattern can lead to what is known as the “pyramid of doom”, especially when using jQuery’s mouse event handlers combined with async operations like $.get or $.post.

Javascript promises: the specification

To fix this and other problems (as we’ll see) with callbacks style of code, a specification has been proposed and it is known under the name CommonJS Promises/A. Let’s see what it says:

A promise represents the eventual value returned from the single completion of an operation. A promise may be in one of the three states, unfulfilled, fulfilled, and failed. The promise may only move from unfulfilled to fulfilled, or unfulfilled to failed. Once a promise is fulfilled or failed, the promise’s value MUST not be changed, just as a values in JavaScript, primitives and object identities, can not change (although objects themselves may always be mutable even if their identity isn’t). The immutable characteristic of promises are important for avoiding side-effects from listeners that can create unanticipated changes in behavior and allows promises to be passed to other functions without affecting the caller, in same way that primitives can be passed to functions without any concern that the caller’s variable will be modified by the callee.
[…]
A promise is defined as an object that has a function as the value for the property ‘then’:
then(fulfilledHandler, errorHandler, progressHandler)
Adds a fulfilledHandler, errorHandler, and progressHandler to be called for completion of a promise. The fulfilledHandler is called when the promise is fulfilled. The errorHandler is called when a promise fails. The progressHandler is called for progress events. All arguments are optional and non-function values are ignored. The progressHandler is not only an optional argument, but progress events are purely optional. Promise implementors are not required to ever call a progressHandler (the progressHandler may be ignored), this parameter exists so that implementors may call it if they have progress events to report.
This function should return a new promise that is fulfilled when the given fulfilledHandler or errorHandler callback is finished. This allows promise operations to be chained together. The value returned from the callback handler is the fulfillment value for the returned promise. If the callback throws an error, the returned promise will be moved to failed state.

It’s very easy to find blog articles and tutorials online, especially around jQuery Deferred object, and almost all of them show how to do callback aggregation using the “then” function to attach callbacks to a promise, whether for success or for errors (or even to signal that an operation has made some progress). When the promise transitions state, the callbacks will be called, that’s as simple as that.
After reading a lot, I thought I knew enough about promises, but then I stumbled upon this page (https://gist.github.com/3889970) by Domenic Denicola, titled “You’re Missing the Point of Promises”, and after reading it I really had the feeling I was missing it entirely!

What promises are really about

As the previously linked page states, Javascript promises are not just about aggregating callbacks, but actually they are mostly about having a few of the biggest benefits of synchronous functions in async code!
Namely

  1. function composition: chainable async invocations
  2. error bubbling: for example if at some point of the async chain of invocation an exception is produced, then the exception bypasses all further invocations until a catch clause can handle it (otherwise we have an uncaught exception that breaks our web app)

To quote Domenic:

The point of promises is to give us back functional composition and error bubbling in the async world. They do this by saying that your functions should return a promise, which can do one of two things:

  • Become fulfilled by a value
  • Become rejected with an exception

And, if you have a correctly implemented then function that follows Promises/A, then fulfillment and rejection will compose just like their synchronous counterparts, with fulfillments flowing up a compositional chain, but being interrupted at any time by a rejection that is only handled by someone who declares they are ready to handle it.

That is, promises have their foundation in this “then” function, if this is broken than the whole mechanism is broken. And that is exactly what is happening with jQuery’s implementation, let’s see why with the help of an explicative (I hope!) code example.

Why jQuery promises are broken

The problem with jQuery’s implementation (up until version 1.9) is that it doesn’t respect the second part of the specification, “This function should return a new promise…”, that is “then” doesn’t return a new promise object when executing one of the handlers (either the fullfillment, the rejection or the progress handler).

This means we cannot do function composition as we don’t have a “then” function to chain to, and we won’t have error bubbling due to a broken chain, the two most important points about this spec.
Finally, what we have is just callback aggregation.

JsFiddle examples

The following fiddles show a simple chain of async functions.
I’m simulating the case where the original promise is fulfilled, the fulfillment handler is invoked, gets the data and then throws an exception in response to it. The exception should be handled by the first rejection handler down the chain.

The first fiddle  is not working as expected: the rejection handler is never invoked and the error bubbles up to the app level, breaking it. Below I show the console reporting the uncaught error:

Uncaught errors

Uncaught errors

Next fiddle  behaves as expected, with the rejection handler correctly invoked. The way to quickly “fix” the broken implementation is to wrap the handler with a new Deferred object that will return a fulfilled/rejected promise, that can be later used for chaining, for example. Below we see the  console showing no uncaught errors.

No errors

No errors

Alternatives

As we have seen, until at least version 1.9.0, jQuery can’t do pomises properly out of the box, but there are several alternatives libraries on the market such as Q, rsvp.js and others, that adhere completely to the specification.

Conclusion

Promises are the present and the future of Javascript asynchronous operations, they provide an elegant and readable code and more importantly they allow function composition and error bubbling, making async more similar to sync programming style, thus making the life of a developer a little bit easier!
I said that Promises are the future of Javascript async programming because Harmony, the next version of Javascript, will allow for great stuff combining promises with Generators. To see a sneak peek preview on how powerful these two concepts can be if used together, point your browsers to http://www.taskjs.org !

Credits

Again, credits to Domenic Denicola for writing this post  and to all the ones who commented and posted examples that helped me understand, notably user jdiamond!

Image voting system using jQuery, Bootstrap and Asp.Net MVC 3, or how to create a jQuery plugin

Standard

Heads up!

The blog has moved!
If you are interested in reading new posts, the new URL to bookmark is http://blog.valeriogheri.com/

 

I was working on a project were one of the requirements was that users had to select a set of images out of a bunch of them, to be furtherly reviewed and commented.
While working on the implementation, I had a sudden idea: I’m going to write for my blog a simple image voting system packed as a jQuery plugin! And so iLikeIt! was born.
The link between the project and the blog article has been Twitter’s Bootstrap, and precisely the Carousel component, as I was using it to iterate between the images.

Live demo and source code

I’ve always wanted to be able to offer live demos of my web applications, and I finally found a fantastic (free) app hosting, appharbor, which also integrates perfectly with Github, so that when I push my changes to Github, it automatically pushes them to appharbor and the new version gets immediately and automatically deployed
on the server!
Click to reach the live demo of iLikeIt!
As always my code is hosted on Github and is free to fork, download etc… (link).

iLikeIt! Screenshot

iLikeIt! screenshot

What iLikeIt! is

iLikeIt! is a simple image voting system that can be used as a classic jQuery plugin, using Bootstrap and jQuery (of course) as base components, and Asp.Net MVC 3 as the web framework to handle server side computation, even though the server side part is really less important here.
The vote is visually expressed with classic “stars” icons, and is 5-stars based.

UI and UX

The idea is to keep the page simple, focusing only on the images and the voting system.
The image rotation is based on a carousel styled component, and to cast a vote a user simply has to mouse over the image and the panel to vote will fade in in the middle of the image, allowing the user to rate the image by clicking on a star.
After the user has casted his vote, the system will display the average rating for the image and won’t allow the user to change his vote (or not until the user refreshes the page… ehi I told you this is a simple system, but you’re free to modify it by cloning and adapting the source code to your needs!).
I know mouse over is not good this days due to mobile experience, and that’s why I will try and cover this topic (and fix the design) in one of my next post.

How to

The HTML part

I wanted the plugin to be dead simple to use, with a jQuery classic approach such as $(“…”).myPlugin().
In the following Gist you can see the HTML code of the View that is needed to use iLikeIt!


@if (false) {
<script src="../../Scripts/jquery-1.7.2-vsdoc.js" type="text/javascript"></script>
<script src="../../JS/iLikeIt.js" type="text/javascript"></script>
}
<div class="container">
<h1>iLikeIt!</h1>
<br />
<h3>iLikeIt! is a simple, proof of concept, image voting system</h3>
<h3>To vote, simply mouse over an image and click on the star you think better fits the image</h3>
<a href="https://github.com/vgheri/iLikeIt"&gt;
<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png&quot; alt="Fork me on GitHub" />
</a>
<br />
<div class="row">
<div class="span8 offset2">
<div id="myCarousel" class="carousel slide">
<div class="carousel-inner">
<div class="active item"><img id="abcjds2230" src="http://dl.dropbox.com/u/6326953/iLikeIt/img01.jpg&quot; alt="First image" /></div>
<div class="item"><img id="abcjds2231" src="http://dl.dropbox.com/u/6326953/iLikeIt/img02.jpg&quot; alt="Second image" /></div>
<div class="item"><img id="abcjds2232" src="http://dl.dropbox.com/u/6326953/iLikeIt/img03.jpg&quot; alt="Third image" /></div>
<div class="item"><img id="abcjds2233" src="http://dl.dropbox.com/u/6326953/iLikeIt/img04.jpg&quot; alt="Fourth image" /></div>
<div class="item"><img id="abcjds2234" src="http://dl.dropbox.com/u/6326953/iLikeIt/img05.jpg&quot; alt="Fifth image" /></div>
<div class="item"><img id="abcjds2235" src="http://dl.dropbox.com/u/6326953/iLikeIt/img06.jpg&quot; alt="Sixth image" /></div>
<div class="item"><img id="abcjds2236" src="http://dl.dropbox.com/u/6326953/iLikeIt/img07.jpg&quot; alt="Seventh image" /></div>
</div>
<a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a>
<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>
</div>
</div>
</div>
</div> <!– /container –>
<!– Le javascript
================================================== –>
<!– Placed at the end of the document so the pages load faster –>
<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/bootstrap.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
<script src="@Url.Content("~/JS/iLikeIt.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
var options = {
postUrl : "/Home/RegisterVote"
}
$("#myCarousel").iLikeIt(options);
});
</script>

view raw

gistfile1.html

hosted with ❤ by GitHub

The main idea is that from the HTML point of view, all that is needed is to setup the Bootstrap’s Carousel component filled in of all the images you need (if you don’t know about Bootstrap and how to setup a Carousel, please read this http://twitter.github.com/bootstrap/javascript.html#carousel ).
It is very important to specify the IDs of the images, as they will be posted to the server to identify the image being voted!
Then, at the bottom of the page, in the script portion, we need to initialize the plugin simply writing


<script type="text/javascript">
$(function () {
var options = {
postUrl : "/Home/RegisterVote"
}
$("#myCarousel").iLikeIt(options);
});
</script>

view raw

gistfile1.js

hosted with ❤ by GitHub

As you can see I’m passing an object called options to the plugin, which contains the URL used for Ajax POST actions to the server.

The javascript part

The baisc idea is to display a small div in the middle of the carousel, between the controls for sliding the images, where we display the star icons and the rating description, so that the user is facilitated when voting.
Here is the js file, which I’ll be covering in more detail immediately after:


//iLikeIt v0.1.0 | (c) 2012 Valerio Gheri | http://www.opensource.org/licenses/mit-license
/*var iLikeIt = */(function ($) {
/*
* Get a text representation of the vote cast by the user
*/
function getStarShortTextDescription(starId) {
var description;
switch (starId) {
case "1":
description = "Awful";
break;
case "2":
description = "Nothing special";
break;
case "3":
description = "Not bad";
break;
case "4":
description = "Nice";
break;
case "5":
description = "I like it!";
break;
default:
description = "Unknown rate";
break;
}
return description;
}
/*
* Show the user its rating by coloring the stars
*/
function displayRating(selectedId, mouseIn) {
var activeImage = $(".active img");
if (activeImage.data("vote") === null || activeImage.data("vote") === undefined) {
var divs = $(".star-container div").slice(0, parseInt(selectedId)).each(function (item) {
$(this).find("img").attr("src", '/Content/images/full_star.png');
});
$(".star-text").text(getStarShortTextDescription(selectedId));
}
}
/*
* Reset the vote panel
*/
function resetStarContainer() {
var divs = $(".star-container div").slice(0, 5).each(function (item) {
$(this).find("img").attr("src", '/Content/images/empty_star.png');
});
$(".carousel-vote-title").text("Vote");
$(".star-text").text("");
}
/*
* Preload the vote panel with the vote already cast by the user, if any.
*/
function preloadRating() {
var activeImage = $(".active img");
var vote = activeImage.data("vote");
resetStarContainer();
if (vote != null && vote != undefined) {
var divs = $(".star-container div").slice(0, parseInt(vote)).each(function (item) {
$(this).find("img").attr("src", '/Content/images/full_star.png');
});
$(".carousel-vote-title").text("Average rating");
$(".star-text").text(getStarShortTextDescription(vote));
}
return true;
}
/*
* Register the vote cast by the user submitting it to the server.
* On success, we display the average rating.
* On error, we display an alert to the user and reset the vote panel
*/
function registerVote(star, postUrl) {
var votedImage = $(".active img");
// If the user didn't vote yet, then we can register the vote
if (votedImage.data("vote") === null || votedImage.data("vote") === undefined) {
$(".carousel-vote-title").text("Submitting your vote…");
$.ajax({
type: 'POST',
url: postUrl,
data: '{"imageId":"' + votedImage.attr("id") + '", "rating":"' + star.attr("id") + '"}',
contentType: "application/json; charset=utf-8",
success: function (msg) {
votedImage.data("vote", msg);
preloadRating();
},
error: function () {
alert("Ooops, an error has occurred. Please try again and if the problem persists… well it's just a proof of concept app, what did you expect?");
resetStarContainer();
return false;
}
});
}
}
function createVotingDiv() {
var votingDiv = '<div class="center carousel-vote">';
votingDiv += '<span class="carousel-vote-title">Vote</span>';
votingDiv += '<span class="star-text"></span>';
votingDiv += '<div class="star-container">';
votingDiv += '<div class="vote-item"><img id="1" class="star" src="/Content/images/empty_star.png")" alt="1" /></div>';
votingDiv += '<div class="vote-item"><img id="2" class="star" src="/Content/images/empty_star.png")" alt="2" /></div>';
votingDiv += '<div class="vote-item"><img id="3" class="star" src="/Content/images/empty_star.png")" alt="3" /></div>';
votingDiv += '<div class="vote-item"><img id="4" class="star" src="/Content/images/empty_star.png")" alt="4" /></div>';
votingDiv += '<div class="vote-item"><img id="5" class="star" src="/Content/images/empty_star.png")" alt="5" /></div>';
votingDiv += '</div></div>';
return votingDiv;
}
$.fn.iLikeIt = function (options) {
var div = createVotingDiv();
// element is the carousel
var element = $(this);
element.append(div);
element.carousel({
interval: false,
pause: "hover"
});
element.carousel('pause');
$(".carousel-vote").on("mouseenter", function () {
preloadRating();
});
$(".vote-item").on("mouseover", "img", function () {
var elem = $(this);
displayRating(elem.attr("id"), true);
});
$(".vote-item").on("mouseout", "img", function () {
var elem = $(this);
//displayRating(elem.attr("id"), false);
resetStarContainer();
});
$(".vote-item").on("click", "img", function () {
var elem = $(this);
registerVote(elem, options.postUrl);
});
return this; //allow chaining
}
} (jQuery));

view raw

iLikeIt.js

hosted with ❤ by GitHub

The code is contained within an anonymouse closure, thus granting privacy and state throughout the lifetime of the application. You may notice that at the top there is a small bit of code commented out, that is because at the beginning I had decided to use the Module pattern (http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript and http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth), but then I chose to simplify the usage of the plugin and changed my mind for anonymouse closure.
The usage of iLikeIt! as a jQuery plugin is possible because of the following code block


$.fn.iLikeIt = function (options) {
var div = createVotingDiv();
// element is the carousel
var element = $(this);
element.append(div);
element.carousel({
interval: false,
pause: "hover"
});
element.carousel('pause');
$(".carousel-vote").on("mouseenter", function () {
preloadRating();
});
$(".vote-item").on("mouseover", "img", function () {
var elem = $(this);
displayRating(elem.attr("id"), true);
});
$(".vote-item").on("mouseout", "img", function () {
var elem = $(this);
displayRating(elem.attr("id"), false);
});
$(".vote-item").on("click", "img", function () {
var elem = $(this);
registerVote(elem, options.postUrl);
});
return this; //allow chaining
}

view raw

gistfile1.js

hosted with ❤ by GitHub

where I add iLikeIt to the set of functions that is possible to access using jQuery “$” notation.
When called, this function creates and injects the HTML to display the voting panel into the Carousel, and then hooks up all the events that are needed, like mouseenter for the voting panel, mouseover, mouseout and click for the star icons.
Whenever a user goes with the mouse over a star icon, a mouseover event will be triggered and handled by the function displayRating


function displayRating(selectedId, mouseIn) {
var activeImage = $(".active img");
if (activeImage.data("vote") === null || activeImage.data("vote") === undefined) {
var divs = $(".star-container div").slice(0, parseInt(selectedId)).each(function (item) {
$(this).find("img").attr("src", '/Content/images/full_star.png');
});
$(".star-text").text(getStarShortTextDescription(selectedId));
}
}

view raw

gistfile1.js

hosted with ❤ by GitHub

which retrieves the active image (using the “active” class attribute that Bootstrap places on the current carousel item) and checks if the image has not yet been voted (more on that later). If that’s the case, then the this function is going to replace all the necessary empty icons with the full star icon, thus providing a visual feedback to the user, along a short text description of the current vote. To easily do this I enumerated the IDs of the star icon with a crescent numbering: the first, that corresponds to vote “1”, has an ID = “1” and so on till 5.
When the user has made up his mind, he can click on the icon that better expresses his thoughts about the image and the mouse event “click” will be triggered and handled by the function registerVote


function registerVote(star, postUrl) {
var votedImage = $(".active img");
// If the user didn't vote yet, then we can register the vote
if (votedImage.data("vote") === null || votedImage.data("vote") === undefined) {
$(".carousel-vote-title").text("Submitting your vote…");
$.ajax({
type: 'POST',
url: postUrl,
data: '{"imageId":"' + votedImage.attr("id") + '", "rating":"' + star.attr("id") + '"}',
contentType: "application/json; charset=utf-8",
success: function (msg) {
votedImage.data("vote", msg);
preloadRating();
},
error: function () {
alert("Ooops, an error has occurred. Please try again and if the problem persists… well it's just a proof of concept app, what did you expect?");
resetStarContainer();
return false;
}
});
}
}

view raw

gistfile1.js

hosted with ❤ by GitHub

This function performs an Ajax POST to the server, sending out the ID of the image and the string representation of the vote.
On success, we need to mark this image as “already voted”, and to do this I’m using the jQuery function “data()”
votedImage.data(“vote”, msg);
where msg holds the average rating that is sent back with the server response, and this value is displayed to the user by calling the preloadRating function.
I think this covers pretty much everything that is important.
The server side code is really short and not significant, but should you want to take a look at that, you can download the source.

Conclusion

We have seen how to create a simple jQuery plugin to build a simple image voting system, and how to use the anonymous closure construct to isolate and organize your javascript code.

Have fun,

Valerio

Drag and Drop asynchronous file upload with HTML 5, File API, XMLHttpRequest and Asp.Net MVC 3

Standard

Heads up!

The blog has moved!
The new URL to bookmark is http://blog.valeriogheri.com/

 

Hello everyone, today I’m going to talk about Drag ‘n Drop in modern web applications (someone said HTML5?).
I got interested in this topic because of a project I’m currently working on: we have a page where the user has to upload an Excel file to load some info into the system and instead of having just a page with the old-fashioned input for browsing the filesystem to pick a file and then press submit, we decided it would have been fun & useful to allow the user to drag and drop the desired file into the page, similar to what you can do with Gmail attachments. After googling, I found out that HTML 5 draft specification includes some new cool features that allow us to easily create a system that gets the job done in a pretty straightforward way. There are already a good number of (jQuery and non) plugins out there that do get the job done (in a much more clever way than my demo does), but I think it can be good fun and very useful to learn how to do it yourself first.

Goal

I’m going to showcase a simple Asp.Net MVC 3 web app that lets a user drag one image file at a time from the desktop and drop it into a special “drop zone” into the web page. The system will then take care of uploading it to the server and, if upload was successfull, it will display to the user the link to access the uploaded image.
As always all the source code is hosted on github at the following URL: https://github.com/vgheri/HTML5Drag-DropExample

What I’ll use

  • Asp.Net MVC 3
  • HTML 5 DnD API, File API and XMLHttpRequest object to handle drag&drop and async file upload
  • jQuery (version 1.7.1 is the latest version at the moment)
  • Bootstrap for UI widgets

Important note

HTML5 and the FileAPI are still draft versions and as such they are subject to change until the final revision comes out and are not supported in all major browsers.
To see exactly what can be used and what not, take a look at the following two websites: http://caniuse.com/  http://html5please.us/.
All the code you will find below has been tested under Google Chrome 16 and IE 9.
Conceptually we can divide the async file upload operation in two different and consecutive moments:

  1. Drag and drop
  2. Async file upload and View update

Part 1: Drag and Drop

This part is all about creating a webpage with a really simple layout: just one dropbox (HTML5 DnD API) where the user can drop an image.
After dropping the image, the web page will load the dropped file in memory (using JS FileAPI) and will show to the user a table with a thumbnail, some basic information like filename and size and a progress bar to provide feedback to the user about the upload operation.
As you can see in the below screenshot, the page I’m going to build is really simple:
Landing page as displayed by Chrome

Landing page as displayed by Chrome

We can start creating something very simple but not that ugly using Bootstrap (I won’t show the Layout code here, you can download the source on github) and the Index page at the beginning looks something like this:

<form id="fileUpload" action="/Home/Upload" method="POST" enctype="multipart/form-data">

    <!-- Main hero unit for a primary marketing message or call to action dropzone="copy f:image/png f:image/gif f:image/jpeg" f:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet f:application/vnd.ms-excel-->
    <div id="dropbox" dropzone="copy f:image/png f:image/gif f:image/jpeg" class="hero-unit">
        <h2 id="droplabel">Drop zone</h2>
        <p id="dnd-notes">Only images file type are supported. Multiupload is not currentrly supported. Once you will drop your image in the dropzone, the upload will start.</p>
        <table id="files-list-table">
            <thead>
                <tr>
                    <th></th>
                    <th>File name</th>
                    <th>File size</th>
                    <th>Upload status</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td id="td-img-preview" class="tableData">
                        <img id="preview" src="" alt="preview will display here" />
                    </td>
                    <td id="td-file-name" class="tableData"></td>
                    <td id="td-file-size" class="tableData"></td>
                    <td id="td-progress-bar" class="tableData">
                        <div id="progressBarBox" class="progress progress-info progress-striped">
                          <div class="bar"></div>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>

        <br/>
        <div id="multiupload-alert"class="alert alert-error">
            <h4 class="alert-heading">Warning!</h4>
            <span id="multiupload-alert-message"></span>
        </div>
        <input id="fileSelect" type="file" name="fileSelect" />
        <p><button id="fallback-submit" type="submit" class="btn btn-primary btn-large">Upload</button></p>
    </div>
</form>
We start to see the first HTML5 thing here with the “dropzone” attribute:
<div id="dropbox" dropzone="copy f:image/png f:image/gif f:image/jpeg" class="hero-unit">
As the draft specification says, it’s purpose is to tell the browser that this element will accept files Drag&Drop.
This attribute can be placed on every HTML element.
Its value specifies which action must be taken upon dropping the file and which MIME types are accepted.
Possible values are:

  1. copy
  2. move
  3. link
I used copy, which indicates that dropping an accepted item on the element will result in a copy of the dragged data, and this value is also the default behaviour.
The second part of the value, “f:image/png f:image/gif f:image/jpeg” indicates the accepted MIME types.
That being said, as of now the dropzone attribute is not currently supported in any of the major browsers, this means that we have to find another way to do it.
Luckily enough, some major browsers support the javascript events related to DnD, like:
drop, dragover or dragexit, so the “logic” will be handled using  javascript!
You can see I also added a classic input type=”file” along with a submit button:
<input id="fileSelect" type="file" name="fileSelect" />
        <p><button id="fallback-submit" type="submit" class="btn btn-primary btn-large">Upload</button></p>
This is there to provide compatibility whenever DnD or FileAPI or async upload (XMLHttpRequest object) are not supported by the browser, as it’s the case for IE9, so we are just fallin back to classic file upload and the user is still able to use the service.
Whenever a fallback is needed, the page displays like the below image
Landing page as displayed by IE9

Landing page as displayed by IE9

So, how do we handle the DnD events and how do we detect if the browser supports FileAPI and async upload?
<script type="text/javascript">
    var fileName = null;
    var fileToUpload = null;

    $(document).ready(function () {
        // init the widgets
        $("#progressBarBox").hide();
        $("#files-list-table").hide();

        // Check if FileAPI and XmlHttpRequest.upload are supported, so that we can hide the old style input method
        if (window.File && window.FileReader && window.FileList && window.Blob && new XMLHttpRequest().upload) {
            $("#fileSelect").hide();
            $("#fallback-submit").hide();
            $("#multiupload-alert").hide();
            var dropbox = document.getElementById("dropbox")
            // init event handlers
            dropbox.addEventListener("dragenter", dragEnter, false);
            dropbox.addEventListener("dragexit", dragExit, false);
            dropbox.addEventListener("dragleave", dragExit, false);
            dropbox.addEventListener("dragover", dragOver, false);
            dropbox.addEventListener("drop", drop, false);
        }
        else {
	$("#multiupload-alert").hide();
            $("#dnd-notes").hide();
        }
    });

    function dragEnter(evt) {
        evt.stopPropagation();
        evt.preventDefault();
    }

    function dragExit(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        $("#dropbox").removeClass("active-dropzone");
    }

    function dragOver(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        $("#dropbox").addClass("active-dropzone");
    }

continues...
The check happens here:
// Check if FileAPI and XmlHttpRequest.upload are supported, so that we can hide the old style input method
        if (window.File && window.FileReader && window.FileList && window.Blob && new XMLHttpRequest().upload)
The first 4 conditions are related to the JS File API, whereas the last is about the async upload. Then if the check is positive, we hide the classic file upload mode and we add event handlers for the dropbox to handle DnD
$("#fileSelect").hide();
$("#fallback-submit").hide();
$("#multiupload-alert").hide();
var dropbox = document.getElementById("dropbox")
// init event handlers
dropbox.addEventListener("dragenter", dragEnter, false);
dropbox.addEventListener("dragexit", dragExit, false);
dropbox.addEventListener("dragleave", dragExit, false);
dropbox.addEventListener("dragover", dragOver, false);
dropbox.addEventListener("drop", drop, false);
Important note: it is not sufficient to just add a handler for the drop event, as it will not properly fire if the others are not set (like WPF DnD events).
I use the dragover and dragexit handlers to modify the dropbox appearance.
Now on to the drop event handler:
function drop(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        $("#dropbox").removeClass("active-dropzone");
        var temp = $("#multiupload-alert");
        // If there is an alert message displayed, we hide it
        $("#multiupload-alert").hide();
        var files = evt.dataTransfer.files;
        var count = files.length;

        // Only call the handler if 1 file was dropped.
        if (count == 1) {
            handleFiles(files);
        }
        else if (count > 1) {
            var message = "Multiupload is not currently supported. Choose only one file.";
            $("#multiupload-alert-message").html(message);
            $("#multiupload-alert").show();
        }
    }
We retrieve the dropped file in the following line of code
var files = evt.dataTransfer.files;
Then if the user dropped only one file, we can go on parsing
function handleFiles(files) {
        var file = files[0];
        if (file.type.match('^image/')) {
            $("#files-list-table").show();
            fileToUpload = file;
            fileName = file.name;
            var reader = new FileReader();

            // init the reader event handlers
            //reader.onprogress = handleReaderProgress; Commented out as we don't really need it
            reader.onloadend = handleReaderLoadEnd;

            // begin the read operation
            reader.readAsDataURL(file);
            this.UploadFile(file);
        }
        else {
            var message = "File type not valid. Only images are allowed.";
            $("#multiupload-alert-message").html(message);
            $("#multiupload-alert").show();
        }
    }
What I do here is to select the first (and only) element from the files array and check that it is of type image (remember that the dropzone attribute doesn’t work).
If the type matches, I can instantiate a new object of type FileReader().This interface provides such an asynchronous API.
The FileReader object has several events that can be catched (http://www.w3.org/TR/FileAPI/#event-handler-attributes-section)
event handler attribute event handler event type
onloadstart loadstart
onprogress progress
onabort abort
onerror error
onload load
onloadend loadend
For the purpose of this tutorial I will just handle the loadend event with the following line of code
reader.onloadend = handleReaderLoadEnd;

FileReader includes four options for reading a file, asynchronously:

  • FileReader.readAsBinaryString(Blob|File) – The result property will contain the file/blob’s data as a binary string. Every byte is represented by an integer in the range [0..255].
  • FileReader.readAsText(Blob|File, opt_encoding) – The result property will contain the file/blob’s data as a text string. By default the string is decoded as ‘UTF-8’. Using the optional encoding parameter it is possible to specify a different format.
  • FileReader.readAsDataURL(Blob|File) – The result property will contain the file/blob’s data encoded as a data URL.
  • FileReader.readAsArrayBuffer(Blob|File) – The result property will contain the file/blob’s data as an ArrayBuffer object.
I’m going to use readAsDataURL() so that I can assign the result of the read operation as the source of the image HTML element I use as a preview.
When the read operation is finished, the loadend event is raised and the following piece of code is executed
function handleReaderLoadEnd(evt) {
        var img = document.getElementById("preview");
        img.src = evt.target.result;
        var fileName = fileToUpload.name;
        if (fileName.length > 20) {
            fileName = fileName.substring(0, 20);
            fileName = fileName + "...";
        }
        $("#td-file-name").text(fileName);
        var size = fileToUpload.size / 1024;
        size = Math.round(size * Math.pow(10, 2)) / Math.pow(10, 2);
        $("#td-file-size").text(size + "Kb");
        $("#progressBarBox").show("fast");
    }
The key here is the result attribute of the FileReader interface.
Having used the readAsDataURL() method, the specification says that
“On getting, if the readAsDataURL read method is used, the result attribute MUST return a DOMString that is a Data URL [DataURL] encoding of the File or Blob‘s data.”
That’s why I’m able to do the following:
var img = document.getElementById("preview");
img.src = evt.target.result;

The rest of the code is to show basic file info to the user into a tabular format.

Part 2: Async file upload and View update

While the file is being read into memory, I can asynchrounously upload the droppped file to the server using the following code
// Uploads a file to the server
    function UploadFile(file) {
        var xhr = new XMLHttpRequest();
        xhr.upload.addEventListener("progress", function (evt) {
            if (evt.lengthComputable) {
                var percentageUploaded = parseInt(100 - (evt.loaded / evt.total * 100));
                $(".bar").css("width", percentageUploaded + "%");
            }
        }, false);

        // File uploaded
        xhr.addEventListener("load", function () {
            $(".bar").css("width", "100%");
        }, false);

        // file received/failed
        xhr.onreadystatechange = function (e) {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {
                    var link = "<a href=\"" + xhr.responseText + "\" target=\"_blank\">" + fileName + "</a>";
                    $("#td-file-name").html(link);
                }
            }
        };

        xhr.open("POST", "/Home/Upload", true);

        // Set appropriate headers
        xhr.setRequestHeader("Content-Type", "multipart/form-data");
        xhr.setRequestHeader("X-File-Name", file.fileName);
        xhr.setRequestHeader("X-File-Size", file.fileSize);
        xhr.setRequestHeader("X-File-Type", file.type);

        // Send the file
        xhr.send(file);
    }
This method is all about the XMLHttpRequest object, which to quote the W3.org spec “allows scripts to perform HTTP client functionality, such as submitting form data or loading data from a remote Web site.”
I use the XMLHttpRequest to send the file in async mode to the server, but before I actually start to send the file, I need to subscribe to a few events which will allow me to give the user feedback about the operation progress.
xhr.upload.addEventListener("progress", function (evt) {
            if (evt.lengthComputable) {
                var percentageUploaded = parseInt(100 - (evt.loaded / evt.total * 100));
                $(".bar").css("width", percentageUploaded + "%");
            }
}, false);
It is important not to confuse the XMLHttpRequest.upload progress event with the XMLHttpRequest progress event, as they serve different purposes.
Having subscribed to this event, I’m able to give feedback to the user about upload operation status.
// File uploaded
        xhr.addEventListener("load", function () {
            $(".bar").css("width", "100%");
        }, false);

        // file received/failed
        xhr.onreadystatechange = function (e) {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {
                    var link = "<a href=\"" + xhr.responseText + "\" target=\"_blank\">" + fileName + "</a>";
                    $("#td-file-name").html(link);
                }
            }
        };
Subscribing to the load event, I’m able to know when all the bytes have been sent.
onreadystatechange is an attribute that represents a function that must be invoked when readyState changes value.
The readyState attribute has 5 possible values
0 Uninitialized
The initial value.
1 Open
The open() method has been successfully called.
2 Sent
The UA successfully completed the request, but no data has yet been received.
3 Receiving
Immediately before receiving the message body (if any). All HTTP headers have been received.
4 Loaded
The data transfer has been completed.
I want to know when the file has been sent and processed by the server
if (xhr.readyState == 4) {
        if (xhr.status == 200) {
                   var link = "<a href=\"" + xhr.responseText + "\" target=\"_blank\">" + fileName + "</a>";
                    $("#td-file-name").html(link);
     }
}
status code 200 means Success, so I can safely read the server response using the responseText property.
I’m using this property to hold the virtual path to the uploaded image, so that the user will be able to retrieve the link to access the remote resource by clicking the link.Now that everything is setup, I can open a connection in async mode (the last parameter), specifying which Controller and which Action will handle the POST
xhr.open("POST", "/Home/Upload", true);
and finally I can start sending bytes
xhr.send(file);
Now all there is left to do is to write the server side code that will receive the file, save it locally and reply to the client with the virtual path to access the resource.
We just have a small problem to deal with: how do we retrieve the posted file when we use XMLHttpRequest.send() method?
HttpPostedFile in this situation will be null, as the content is not held into Request.Files object, but instead it is stored into Request.InputStream as a stream of bytes!
[HttpPost]
        public string Upload()
        {
            UploadedFile file = RetrieveFileFromRequest();
            string savePath = string.Empty;
            string virtualPath = SaveFile(file);

            return virtualPath;
        }

        private UploadedFile RetrieveFileFromRequest()
        {
            string filename = null;
            string fileType = null;
            byte[] fileContents = null;

            if (Request.Files.Count > 0)
            { //we are uploading the old way
                var file = Request.Files[0];
                fileContents = new byte[file.ContentLength];
                file.InputStream.Read(fileContents, 0, file.ContentLength);
                fileType = file.ContentType;
                filename = file.FileName;
            }
            else if (Request.ContentLength > 0)
            {
                // Using FileAPI the content is in Request.InputStream!!!!
                fileContents = new byte[Request.ContentLength];
                Request.InputStream.Read(fileContents, 0, Request.ContentLength);
                filename = Request.Headers["X-File-Name"];
                fileType = Request.Headers["X-File-Type"];
            }

            return new UploadedFile()
            {
                Filename = filename,
                ContentType = fileType,
                FileSize = fileContents != null ? fileContents.Length : 0,
                Contents = fileContents
            };
        }

I’m not displaying the code for method SaveFile()  and class UploadedFile, you can download the suource code should you need it.

And this is the result:

Result

Result

Conclusions

With this post I just scratched the surface, there’s much more about FileAPI and XMLHttpRequest: for example using FileAPI it is possible to pause and resume an upload as it supports file chunking.
A useful jQuery plugin that leverage the full potential of the technologies shown here is available at http://blueimp.github.com/jQuery-File-Upload/

Custom Unobtrusive jQuery Validation with Data Annotations in MVC 3

Standard

Heads up!

The blog has moved!
The new URL to bookmark is http://blog.valeriogheri.com/

 

MVC 3 introduced what is called Unobtrusive Client Validation. This feature relies on jQuery and HTML5 custom data attributes. It is very powerful and not so hard to use… with the right tutorial to follow 🙂
It’s also very useful because it leverages the power of Data Annotations,  a clean way to express View Model rules in my opinion.
Let’s start the tutorial with a bit of theory (if you’re not interested, you can skip it and go straight to the paragraph “The tutorial”  and download the source code from https://github.com/vgheri/DateCustomValidationExample)

What is a custom data attribute

“As per HTML 5 draft specification a custom data attribute is an attribute in no namespace whose name starts with the string “data-“, has at least one character after the hyphen, is XML-compatible, and contains no characters in the range U+0041 to U+005A (LATIN CAPITAL LETTER A to LATIN CAPITAL LETTER Z). Custom data attributes are intended to store custom data private to the page or application, for which there are no more appropriate attributes or elements. These attributes are not intended for use by software that is independent of the site that uses the attributes. It would be inappropriate, however, for the user to use generic software not associated with that music site to search for tracks of a certain length by looking at this data. This is because these attributes are intended for use by the site’s own scripts, and are not a generic extension mechanism for publicly-usable metadata. Every HTML element may have any number of custom data attributes specified, with any value. JavaScript libraries may use the custom data attributes, as they are considered to be part of the page on which they are used.”

This means that any attribute whose name starts with “data-” will be treated as a storage area for private data.
This allows you to write valid HTML markup (passing an HTML 5 validator) while, simultaneously, embedding data within your page.

MVC 3 + jQuery

MVC3’s new jQuery Validation mechanism links jQuery Validation and Validation Attributes Metadata. The magic happens in the jquery.validate.unobtrusive file that takes all data- attributes and works with them.
To exploit this mechanism, we need to create our own Custom Validation Attributes as we’ll see in this article.

How MVC generates the data- attributes:

The custom data attributes are generated by helpers methods like Html.TextBoxFor(), as it knows already all the Data Anotations that the field needs, and if Unobstrutive setting is true (by default in MVC 3) it will generate all data- that he needs.
For each client validation rule(we’ll get there), an attribute is added with data-val-rulename=”message”. Then, for each parameter in the client validation rule, an attribute is added with data-val-rulename-paramname=”paramvalue”.

The tutorial

In this tutorial we will create a custom validation that checks if a certain date is greater than another date entered by the user in a form. For the purpose of this tutorial we will create a model consisting of one class, Project, that obviously has a start date and an end date. Our goal is to force the user to enter an end date greater than the start date of the project.
To better clarify how to create a custom validator, this tutorial is divided in two steps: first we will provide server side validation, and on top of that we will add client side validation.

Step 1: Server side validation

First we create the model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace DateCustomValidationExample.Models
{
    public class Project
    {
        public string Name { get; set; }

        public string ProjectManager { get; set; }

        [DisplayName("Start date")]
        [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        public DateTime StartDate { get; set; }

        [DisplayName("Estimated end date")]
        [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        [DateGreaterThan("StartDate", "Estimated end date must be greater than the start date of the project")]
        public DateTime EndDate { get; set; }
    }
}
Note that we already added our custom data attribute data annotation even if it’s not yet created. In my opinion this helps us to think more about how we want to use it rather than how it should be built.
Ok, we defined how we want to use the attribute, now we must create it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace DateCustomValidationExample.Models
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
    public class DateGreaterThanAttribute : ValidationAttribute
    {
        string otherPropertyName;

        public DateGreaterThanAttribute(string otherPropertyName, string errorMessage)
            : base(errorMessage)
        {
            this.otherPropertyName = otherPropertyName;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            ValidationResult validationResult = ValidationResult.Success;
            try
            {
                // Using reflection we can get a reference to the other date property, in this example the project start date
                var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
                // Let's check that otherProperty is of type DateTime as we expect it to be
                if (otherPropertyInfo.PropertyType.Equals(new DateTime().GetType()))
                {
                    DateTime toValidate = (DateTime)value;
                    DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
                    // if the end date is lower than the start date, than the validationResult will be set to false and return
                    // a properly formatted error message
                    if (toValidate.CompareTo(referenceProperty) < 1)
                    {
                        validationResult = new ValidationResult(ErrorMessageString);
                    }
                }
                else
                {
                    validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
                }
            }
            catch (Exception ex)
            {
                // Do stuff, i.e. log the exception
                // Let it go through the upper levels, something bad happened
                throw ex;
            }

            return validationResult;
        }
    }
}

Notice how the attribute extends ValidationAttribute and overrides IsValid method, in which we perform the custom validation.

Now we need aController and aView to test the validation.

Create a HomeController with a simple Index action method, build the project and then add a strongly typed View bound to our model, choosing the “Create” item into the scaffolding  menu.
To simplify user interaction, we will use the standard  jQuery datepicker component to let the user choose the dates.
In order to use it, we must add the following references to our page (or into the master layout page)
<script src="../../Scripts/jquery-1.x.x.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-ui-1.x.x.js" type="text/javascript"></script>
<link href="../../Content/themes/base/jquery.ui.all.css" rel="stylesheet" type="text/css" />
Where x.x is the version you currently have installed.
To bind the datepicker method to our input fields, we use the following jquery code
<script>
    $().ready(function () {
        $("#StartDate").datepicker();
        $("#EndDate").datepicker();
        return false;
    });
</script>

Then, in order to use server side validation, we need to add the following code to our HomeController to handle form submit

// POST: /Home/
[HttpPost]
public ActionResult Index(Project model)
{
   return View(model);
}
If we run the project and inspect the HTML emitted, this is what we see:
HTML5 emitted  with the server side validation

HTML5 emitted with the server side validation

As expected there is no custom data attribute related to our custom rule.

Then we can insert two dates that will trigger the error message and we get the error message as we expected
Validation error message

Validation error message

That’s it for server-side validation.

Client side validation

Now on to the second part of this tutorial, where we will achieve client side validation.
Before we start, a few important notes (taken directly from the blog  of one of the developers of Asp.Net MVC 3)

jQuery Validate requires your input elements to be inside of a <form> element in order to be validated. In addition, MVC 3 requires that you have called Html.BeginForm() to render this form, so that it can find its book-keeping object to help render the HTML attributes.

Writing a client-side validator involves two steps:

  1. adding one more method to our DateGreaterThanAttribute
  2. writing some jQuery code to create a validation rule and what is called an adapter, which takes the parameter values from the HTML attributes and turns it into jQuery Validate metadata.
Note that we need to write a custom adapter since the default collection doesn’t come with what we need! Otherwise, there is an adapter collection available at jQuery.validator.unobtrusive.adapters.
Let’s go back to our DateGreaterThanAttribute class and modify it so that it implements the IClientValidatable, which, as MSDN says, provides a way for the ASP.NET MVC validation framework to discover at run time whether a validator has support for client validation.
Since we are implementing it, we need to write the method GetClientValidationRules like the following:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            //string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
            string errorMessage = ErrorMessageString;

            // The value we set here are needed by the jQuery adapter
            ModelClientValidationRule dateGreaterThanRule = new ModelClientValidationRule();
            dateGreaterThanRule.ErrorMessage = errorMessage;
            dateGreaterThanRule.ValidationType = "dategreaterthan"; // This is the name the jQuery adapter will use
            //"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
            dateGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);

            yield return dateGreaterThanRule;
        }

Important: The metadata expressed for this rule will be used by the runtime to emit the HTML5 data-val custom attributes!

Now we need to create the jQuery validation function and the jQuery adapter.
We can create a separate .js file, thus we can reuse the code.
Let’s create a new Folder into /Scripts and call it “Custom”, then let’s create a new script called CustomValidation.js.
Let’s see the code:
/* File Created: January 16, 2012 */

// Value is the element to be validated, params is the array of name/value pairs of the parameters extracted from the HTML, element is the HTML element that the validator is attached to
$.validator.addMethod("dategreaterthan", function (value, element, params) {
    return Date.parse(value) > Date.parse($(params).val());
});

/* The adapter signature:
adapterName is the name of the adapter, and matches the name of the rule in the HTML element.

params is an array of parameter names that you're expecting in the HTML attributes, and is optional. If it is not provided,
then it is presumed that the validator has no parameters.

fn is a function which is called to adapt the HTML attribute values into jQuery Validate rules and messages.

The function will receive a single parameter which is an options object with the following values in it:
element
The HTML element that the validator is attached to

form
The HTML form element

message
The message string extract from the HTML attribute

params
The array of name/value pairs of the parameters extracted from the HTML attributes

rules
The jQuery rules array for this HTML element. The adapter is expected to add item(s) to this rules array for the specific jQuery Validate validators
that it wants to attach. The name is the name of the jQuery Validate rule, and the value is the parameter values for the jQuery Validate rule.

messages
The jQuery messages array for this HTML element. The adapter is expected to add item(s) to this messages array for the specific jQuery Validate validators that it wants to attach, if it wants a custom error message for this rule. The name is the name of the jQuery Validate rule, and the value is the custom message to be displayed when the rule is violated.
*/
$.validator.unobtrusive.adapters.add("dategreaterthan", ["otherpropertyname"], function (options) {
    options.rules["dategreaterthan"] = "#" + options.params.otherpropertyname;
    options.messages["dategreaterthan"] = options.message;
});

Let’s look at it in more details:
$.validator.addMethod("dategreaterthan", function (value, element, params) {
    return Date.parse(value) > Date.parse($(params).val());
});
The first parameter for addMethod is the name that identifies the rule and can be different from what we used inside GetClientValidationRules => dateGreaterThanRule.ValidationType = “dategreaterthan”;
In this example is the same, but you can change it.
The second parameter is the actual validator, in this case simply a comparison between two dates.

The adapter instead is tightly coupled with GetClientValidationRules implementation, therefore the first parameter must match the name we used in the code of our custom attribute for the validationType.

The second parameter is an array of parameters name.

In our case we only added one parameter

dateGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
so our array will have only one parameter. Should we have more, it would look like [“param1”, “param2”, ….]
The third parameter is a function which is called to adapt the HTML attribute values into jQuery Validate rules and messages.
options.rules["dategreaterthan"] //"dategreaterthan", the key to identify the rule, must match what we used in the addMethod as first parameter.
options.rules["dategreaterthan"] = "#" + options.params.otherpropertyname;

we use the # so that the value stored will be something like “#StartDate”, that is ready to be consumed by the validation rule previously wrote to get the value out of the StartDate field.

The javascript is done! Now let’s go back to our View because we need to add all the references we need to run the unobtrusive validation:
<script src="../../Scripts/jquery.validate.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/Custom/CustomValidation.js" type="text/javascript"></script>

Everything’s done and now we can run the app. If we inspect the HTML that has been emitted this is what we see

HTML emitted with client side validation

HTML emitted with client side validation

and if we run the application we will see the same error message as before, with no form submit!

The source code is available on my github at the following URL: https://github.com/vgheri/DateCustomValidationExample

That’s all for the unobtrusive custom validation with MVC 3.

I hope this helps,

Valerio


Enabling jQuery IntelliSense in an MVC project with Visual Studio 2010

Standard

Heads up!

The blog has moved!
The new URL to bookmark is http://blog.valeriogheri.com/

 

Hello,
today I’d like to share with you a quick trick to enable IntelliSense to help writing jQuery code in the View.
If we are using the out of the box convention of MVC 3, our layout code will be placed in the Views/Shared/_layout.cshtml folder, and inside it we can put all of our scripts and stylesheets global references.

Then, when we write jQuery code in our own View, we will notice that we have no IntelliSense support helping us, contrary to our expectations.
The problem lies in the way Visual Studio works: IntelliSense engine will not be able to access the references we made into _layout because the engine doesn’t really know which layout will be used to render the View until the code is running (just imagine a scenario where we may dynamically choose a layout based on user profile to understand why Visual Studio works this way).

The solution

The first solution I came up with was to add again the vsdoc script reference (jquery-X.Y.Z-vsdoc.js) to the View and I had IntelliSense again.

Enabling Intellisense

Habemus IntelliSense!

The downside of this is double references that will produce double requests when browsing the site, and this is bad!

So I googled and I found a pretty nice trick to enable Intellisense and to avoid downsides(link):

@if (false)
{
<script src="../../Scripts/jquery-1.7.1-vsdoc.js" type="text/javascript"></script>
}

Surrounding the script reference by an if (false) statement, the reference will never be posted to the browser because the statement will always be false, but we will still have our Intellisense to help us!

Twitter Real Time Search

Standard

Heads up!

The blog has moved!
The new URL to bookmark is http://blog.valeriogheri.com/

 

Hello,I’ve always developed web applictions using the ASP.Net Web Forms framework, and so far everything has been ok. A few weeks ago, though, I came up with the idea to re-engineer one of the most important web application we use at work. This web platform runs the business, has been developed using the Web Forms framework and is quite old fashioned: it’s starting to feel a little cramped when we have to add new features and new UI components that should handle complex user interactions with the UI. Not to mention that is not very usable on tablets like the iPad.

Looking for a solution, I thought it would have been great to use the latest available technologies to re-engineer the system… and therefore MVC!

I started learning the ASP.Net MVC framework, along with jQuery, reading the MVC Music Store tutorial .

After that I decided to give it a try implementing a web application on my own, an application that lets the user query the Twitter real time feed.

I had no idea where to start from, I just liked the idea of using the Twitter API!

I’ll start saying that in the following tutorial there’s not much of the true MVC potential about model binding and validation, nevertheless it has been a starting point to deal with such technologies like jQuery, Json, and the foundamentals of MVC.Before we start, I want to point out that this article is not a “Get Started“ tutorial, therefore to all of you that start from scratch I strongly suggest to start reading the MVC Music Store tutorial using the link posted above, otherwise you will miss some key concepts.

Furthermore, I will not post a lot of code here because it’s not really that readable, but at the end of the post you will find a link to download the source code of the project so that you will be able to follow and modify the code as you wish.

Objective

The goal is to develop a web page that lets a single user (to make things simple) see Tweets related to a topic he’s interested in, as they are being created in real time.

Given the single user restriction (later on I’ll explain why), this application is obviously just a toy, nevertheless it’s still useful to cover the basics and could be easily converted into something more useful.

Use case

The user enters a keyword in the search bar and press the “Search” button. After a while, if someone is twitting something related to the user’ search key, tweets will appear inside a box, piling up as they are being created.

When the user is satisfied, he halts the search by clicking the “Stop” button.

What I will use

ASP.Net MVC 3 framework, jQuery and the Twitterizer library (http://www.twitterizer.net/)

Realization

The Twitter developer website is full of interesting and useful stuff if you want to go into more details then the one you will find here.

To start off we must create an application on the Twitter Developer website to start using the Twitter API. Follow the instructions on the dev website and then do as shown in the following screenshot.

How to setup creation of the app

How to setup creation of the app

At the end of the process, you will receive the authorisation keys for the 0Auth authentication protocol.

To handle authentication and web request processes, I’m usingTwitterizer, an ad-hoc library that can be found here.

At the time I’m writing this, current version is 2.3.3, but it’s not correctly working as the address for the stream in the API is changed and is now using the HTTPS protocol. So I had to download the source code, change the address (TwitterStream.cs, row numbers 210 and 212) and import the Twitterizer.Streaming project into my own solution (I liked also the idea of looking at the code).It’s now time to decide how to architecture the project!

Even for simple and quick projects, I like to use a layered architecture, so for this project I created:

  • a Domain Model layer
  • a Data access layer that handles access to the storage to add and retrieve tweets
  • an Application Logic layer that will handle main user operations and access to the Twitterizer service
  • a Presentation layer, built using the MVC framework
What do you say, it’s too much architecture  for such a small project?
Let me show you something 😀
The general problem

The general problem

The Domain Model layer

It’s very thin given that the domain can be narrowed down to our representation of a Tweet. The same representation will  also be suitable for the View. Let’s see the class diagram:

Domain Model class diagram

Domain Model class diagram

Following the code for the Tweet class.
namespace Model
{
    public class Tweet
    {
        public decimal Id { get; set; }
        public string Username { get; set; }
        public string ProfileImageURL { get; set; }
        public string Text { get; set; }
        public string Timestamp { get; set; }
        public long? NumberOfFollowers { get; set; }

        /// <summary>
        /// Side-effect free function. Extract URLs, hashtag and mention patterns inside a text and convert them to HTML links
        /// </summary>
        /// <param name="tweetText"></param>
        /// <returns></returns>
        public string FormatTweetText()
        {
            string formattedTweetText = string.Empty;
            formattedTweetText = Regex.Replace(this.Text, @"(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])",
               delegate(Match match)
               {
                   return string.Format("<a href=\"{0}\">{0}</a>", match.ToString());
               }, RegexOptions.IgnoreCase);

            formattedTweetText = Regex.Replace(formattedTweetText, @"#+[a-zA-Z0-9]*",
               delegate(Match match)
               {
                   return string.Format("<a href=\"http://twitter.com/#!/search?q={0}\">{1}</a>", match.ToString().Replace("#", "%23"), match.ToString());
               }, RegexOptions.IgnoreCase);

            formattedTweetText = Regex.Replace(formattedTweetText, @"@+[a-zA-Z0-9]*",
               delegate(Match match)
               {
                   string mention = match.ToString().Substring(1);
                   return string.Format("<a href=\"http://twitter.com/#!/{0}\">{1}</a>", mention, match.ToString());
               }, RegexOptions.IgnoreCase);

            return formattedTweetText;
        }
    }
}

The Data Access Layer

This layer is based on the Repository pattern, albeit in a very simplified form, and the repository is created by a factory, using the Abstract Factory pattern.

Let’s first take a look at the class diagram:

Data Access Layer class diagram

The design is pretty easy and straightforward: the concrete repositories instances must implement the ITwitterRepository interface, therefore allowing for an easy swap amongst concrete repositories and no code change is required in the upper layers.
We can retrieve either a list of tweets either a single tweet at a time, using GetRange or GetStatus methods.
Both take a parameter called lastId: this is because we only want to display new tweets and not duplicates.

The concrete repository is created by the RepositoryFactory object, implementing the IRepositoryFactory interface.

In more complex projects, and more susceptible to changes, it would probably be a good idea to use an Inversion of Control tool.

For the purpose of this project I created a concrete repository that works in memory and mimics a queue, and the queue is a static object itself. Thus the single user limitation.
As you can see I didn’ t put a lot of effort in this because it was not really the point where I wanted to focus on. Using a database would be a better idea, or whatever else comes to your mind.

The Application Logic layer

Responsible for handling user generated operations (i.e. start and stop capturing Twitter real time streaming) and subsequent automatically generated requests (more on that later). As shown in the class diagram, there are two object of particular interest, TwitterAccessService and TwitterStreamingListener.

Application Logic Layer class diagram

Application Logic Layer class diagram

The former, as the name implies, implements the Service pattern and is the interface towards the Presentation Layer, the latter is a wrapper for the Twitterizer API. It’s designed as a Singleton (here lies the second cause for  the single user limitation) and is using dependancy injection on the factory method to know which repository to use when a new Tweet or a new event arrives.
Let’s see the code:
namespace ApplicationLogic
{
    public class TwitterStreamingListener
    {
        private static TwitterStreamingListener _instance = null;
        private ITweetsRepository _repository;
        private List<string> _otherEvents;
        private bool _stopSearch;
        private ModelTranslationService _translationService;
        static OAuthTokens tokens = new OAuthTokens()
        {
            ConsumerKey = "yourConsumerKey",
            ConsumerSecret = "yourConsumerSecret",
            AccessToken = "yourAccessToken",
            AccessTokenSecret = "yourAccessTokenSecret"
        };

        public static TwitterStreamingListener GetInstance(ITweetsRepository repository)
        {
            if (_instance == null)
            {
                _instance = new TwitterStreamingListener(repository);
            }
            return _instance;
        }

        private TwitterStreamingListener(ITweetsRepository injected)
        {
            this._stopSearch = false;
            this._repository = injected;
            this._otherEvents = new List<string>();
            this._translationService = new ModelTranslationService();
        }

        public void StartCaptureStreaming(SearchParameters parameters)
        {
            StreamOptions options = new StreamOptions();
            options.Track.Add(parameters.SearchKey);
            this._stopSearch = false;

            TwitterStream stream = new TwitterStream(tokens, "RTSearch (Dev)", options);

            IAsyncResult result = stream.StartPublicStream(
                StreamStopped,
                NewTweet,
                DeletedTweet,
                OtherEvent
            );

            while (!this._stopSearch)
            {

            }

            stream.EndStream(StopReasons.StoppedByRequest, "Stop by user");
        }

        public void StopCaptureStreaming()
        {
            this._stopSearch = true;
        }

        void StreamStopped(StopReasons reason)
        {
            if (reason == StopReasons.StoppedByRequest)
            {
                this._repository.Clear();
                this._stopSearch = true;
            }
            else
            {
                //Do something...
            }
        }

        void NewTweet(TwitterStatus twitterizerStatus)
        {
            Tweet tweet = this._translationService.ConvertToViewModel(twitterizerStatus);
            this._repository.Add(tweet);
        }

        void DeletedTweet(TwitterStreamDeletedEvent e)
        {
            this._repository.Delete(e.Id);
        }

        void OtherEvent(TwitterStreamEvent e)
        {
            this._otherEvents.Add(e.EventType);
        }
    }
}
The static initialization block of code is where you have to put the 0Auth tokens obtained by registering the application on the Twitter Dev site.
In the StartCaptureStreaming method we setup the search key with the following code
StreamOptions options = new StreamOptions();
options.Track.Add(parameters.SearchKey);
TwitterStream stream = new TwitterStream(tokens, "RTSearch (Dev)", options);
And then we start the stream providing callbacks for main events.
IAsyncResult result = stream.StartPublicStream(
                StreamStopped,
                NewTweet,
                DeletedTweet,
                OtherEvent
            );
Whenever a new Tweet arrives, we simply add it to the repository:
void NewTweet(TwitterStatus twitterizerStatus)
{
   Tweet tweet = this._translationService.ConvertToViewModel(twitterizerStatus);
   this._repository.Add(tweet);
}

The repository is injected by the TwitterAccessService object, getting an instance for the repository object by the Factory.Furthermor, TwitterStreamingListener uses the ModelTranslationService object to translate from Twitterizer domain to our application domain model.

These two last objects mimic the behaviour of a (minimal and incomplete) Anti Corruption layer.

The Presentation Layer

This application has only one page to show to the user, and all of the possible actions happens inside it. Therefore the presentation layer will be really thin and simple: 1 page and 1 controller to handle actions.

As I said at the beginning of this article, there will not be much about model binding and validation, a very strong point of the MVC framework.

This is because I chose to use jQuery to load new tweets at runtime.

The page layout is really simple, with a search bar and two buttons to accept user inputs and a box to contain new tweets.

Here is how it looks like:
Search bar and tweet box

Search bar and tweet box

As you can see the page is really simple and clean, with a search bar, two buttons to star and stop the search, and a tweet box to display tweets. Tweets can pile up allowing vertical scrool bar to appear.

The html code is pretty straightforward, so I won’t cover it.

Let’s see the meaning of some of the variables declared in the javascript block of code:

var urlBase = 'http://twitter.com/';
var serviceStarted = false;
var timeOut = 0;
var singleTweetMode = false;
var last = 0;

“serviceStarted” is used to signal the javascript code that the user started a search.

“singleTweetMode” can be set to true to mean that we will display inside the tweets box one new tweet at a time, viceversa we will display several new tweets at a time, therefore pushing down all the old ones.

“last” is used to store the Id of the last tweet that has been shown, so that tweets will be shown respecting their original timeline.

Now let’s see the code in detail based on the different actions performed by the user.

Search

after pressing the Search button, the control passes to the javascript run() function, which, after validating user input, signal to the page that the service is started via the “serviceStarted = true” line of code.

Then, based on the operational mode that we chose via the singleTweetMode, tells the javascript engine that every x milliseconds it should invoke the poll() function.

After that, the application performs an Ajax postback with jQuery, passing the search key as a parameter for the HomeController hosted StartSearch action method, that will then ask the TwitterAccessService to start searching for tweets based on the keyword

function run() {
        if (!serviceStarted) {
            var value = $('input:text[name=searchKey]').val();
            if (value == null || value == '') {
                alert("Please enter a search keyword!");
                return false;
            }
            $('#SearchButton').val('Searching');
            serviceStarted = true;
            if (singleTweetMode) {
                timeOut = window.setInterval(poll, 2000);
            }
            else {
                timeOut = window.setInterval(poll, 10000);
            }
            //$('#SearchButton').attr('disabled', 'true');
            $.post('/Home/StartSearch', { searchKey: value });
        }
}
The HomeController will then invoke the service and then the TwitterStreamingListener object’ StartCaptureStreaming method.
The javascript poll() function therefore performs an Ajax postback to the GetTweets action method (hosted by the HomeController), using function displayNewTweets as a callback to invoke when data has been returned.
$('#LoadingImage').css("display", "inline");
$.post('/Home/GetTweets', { lastId: last }, displayNewTweets);
Let’s now see the code for the GetTweets action method
//
// Ajax: /Home/GetTweets
[HttpPost]
public ActionResult GetTweets(decimal lastId)
{
     JsonResult serializedTweets;
     if (singleTweetMode)
     {
         if (ViewBag.Count == null)
         {
              ViewBag.Count = 0;
         }
         else
         {
              ViewBag.Count++;
         }
         var tweet = service.GetTweet(lastId);
         serializedTweets = Json(tweet);
      }
      else
      {
         if (ViewBag.Count == null)
         {
             ViewBag.Count = 0;
         }
         else
         {
              ViewBag.Count++;
         }
         var tweets = service.GetTweets(lastId);
         serializedTweets = Json(tweets);
       }
       return serializedTweets;
 }
It’s very simple: it invokes the right service method based on the mode (a variable present also in the HomeController) whether it’s for single tweet or several at once, and then serializes the result in Json notation to give the control back to the callback javascript function displayNewTweets.

I serialized data in Json format in order to return data to javascript in a way that it’s simple for it to manage and natively supported by the framework.

Type JsonResult is of type ActionResult, so the method’ signature is still valid.

Upon returning from Post, the displayNewTweets javascript function is invoked, creating the necessary HTML code to display retrieved tweets:
function displayNewTweets(data) {
        $.each(data, function (count, item) {
            if (last < item.Id) {
                last = item.Id;
                addNew(item);
            }
        });
        $('#LoadingImage').css("display", "none");
}

function addNew(item) {
   $('#TweetsBody').prepend(renderTweet(item, 'hidden'));
}

function renderTweet(item) {
        importanceColor = getImportanceColor(item.NumberOfFollowers);
        return '<div class="tweet" id="' + item.ID + '">' +
               '<strong><a href="' + urlBase + item.Username + '" style="color:' + importanceColor + '">' +
               '<img src="' + item.ProfileImageURL + '" class="ProfileImg" alt="Usr image" />' +
               item.Username + '</a></strong><span class="text">' + ' ' + item.Text +
               '</span><span class="created_at"><br /><a href="' + urlBase + '#!/' + item.Username + '/status/' + item.ID + '">' +
               item.Timestamp.toString() + '</span></div>';
}

function getImportanceColor(number) {
        rgb = 255 - Math.floor(16 * (Math.log(number + 1) + 1));
        return 'rgb(' + rgb + ', 0, 0)';
}
In the addNew function the new tweet is put on top of the list, using the “prepend” built-in function.
Function “renderTweet” is just a matter of building up the HTML code to display the new tweet. The username is colored based on the importance of the user, and the importance is based on the number of followers.

Stop search

When the user presses the Stop button, the javascript stop function is invoked

function stop() {
        if (serviceStarted) {
            $('#SearchButton').val('Search');
            $('#StopButton').val('Stopping');
            window.clearInterval(timeOut);
            $.post('/Home/StopSearch');
            $('#StopButton').val('Stop');
            //$('#SearchButton').attr('disabled', 'false');
        }
}

The if statement prevents the user to perform an actual Stop operation even though no search is currently running.

After the check I need to clear the timer that is causing the poll function to be invoked over and over

window.clearInterval(timeOut);

Then I need to perform a post to notify the Twitterizer lib to close the stream and release the resources. To make this operation easy I chose to use the Singleton pattern so that only one stream can be open at once, and therefore it’s easy to retrieve its reference.

References

To write this tutorial I actually used and got inspired by a several web tutorial, stackoverflow posts and books. Some of them are:

The source code can be downloaded here .

That’s it, I really hope you enjoyed this article!

Valerio