Friday, October 14, 2011

Building A Ratings Widget With ASP.NET MVC And jQuery


Rating systems are all over the web today. They’re an easy and convenient way to give your users the ability to rate what their reading on the page. In ASP.NET WebForms you can use the Ajax Control Toolkit to add a ratings system to your site, but if you’re using ASP.NET MVC, this won’t work. I thought it would be good to show you one possible way of doing this with ASP.NET MVC and jQuery. The example I’m demonstrating in this article is using the new jQuery 1.4 Alpha 2 release. You can download the jQuery library from here. The end result will look like the one shown in the video below.
[YOUTUBE VIDEO]




 
To demonstrate this I’ve created an ASP.NET MVC application. The script to add this functionality to your site is very small, so let’s jump straight into it. The basic concept is a web page has a series of stars on a page. The user can hover their mouse over each star. By doing this the star will be highlighted. Whatever star they click on is the rating they want to give the content they’re rating. Here’s the code below:
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <script language="javascript" type="text/javascript" src="../../Scripts/jquery-1.4a2.js"></script>
    <script language="javascript" type="text/javascript">
        $(function() {
            $("img").mouseover(function() {
                giveRating($(this), "FilledStar.png");
                $(this).css("cursor""pointer");
            });
 
            $("img").mouseout(function() {
                giveRating($(this), "EmptyStar.png");
            });
 
            $("img").click(function() {
                $("img").unbind("mouseout mouseover click");
 
                // call ajax methods to update database
                var url = "/Rating/PostRating?rating=" + parseInt($(this).attr("id"));
                $.post(url, nullfunction(data) {
                    $("#result").text(data);
                });
            });
        });
 
        function giveRating(img, image) {           
            img.attr("src""/Content/Images/" + image)
               .prevAll("img").attr("src""/Content/Images/" + image);
        }
    </script>       
    <p>
        <img src="../../Content/Images/EmptyStar.png" alt="Star Rating" align="middle" id="1" />
        <img src="../../Content/Images/EmptyStar.png" alt="Star Rating" align="middle" id="2" />
        <img src="../../Content/Images/EmptyStar.png" alt="Star Rating" align="middle" id="3" />
        <img src="../../Content/Images/EmptyStar.png" alt="Star Rating" align="middle" id="4" />
        <img src="../../Content/Images/EmptyStar.png" alt="Star Rating" align="middle" id="5" />
    </p>
    <div id="result"></div>
</asp:Content>
To create the rollover effect, I’ve created an event handler for the mouseover and mousreout events which calls the giveRating function. These pass the name of an image to display when these events are fired:
$("img").mouseover(function() {
giveRating($(this), "FilledStar.png");
      $(this).css("cursor""pointer");
});
 
$("img").mouseout(function() {
giveRating($(this), "EmptyStar.png");
});
The giveRating function handles all the magic in regards to selecting all stars up to and including the current star the user is hovering over. 
function giveRating(img, image) {           
img.attr("src""/Content/Images/" + image)
            .prevAll("img").attr("src""/Content/Images/" + image);
}
This prevAll method finds all sibling <img> elements in front of the current <img> element and changes their image:
.prevAll("img").attr("src""/Content/Images/" + image)
When the user is ready they can click on any star and the result will be posted to the backend using jQuery’s post method. 
$("img").click(function() {
$("img").unbind("mouseout mouseover click");
 
      // call ajax methods to update database
      var url = "/Rating/PostRating?rating=" + parseInt($(this).attr("id"));
      $.post(url, nullfunction(data) {
            $("#result").text(data);
});
});
 
Notice how I’m calling unbind in the code above? 
 
$("img").unbind("mouseout mouseover click");
To ensure the rating only happens once, I’m using jQuery’s unbind method remove the mouseovermouseout and click events. Then I post the selected rating to theRating/PostRating action method. The action method returns some JSON stating how many stars the user rated this: