ASP.NET MVC Application Building: Forums #6 – Ajax

In this series of blog entries, I build an entire MVC Forums application from start to finish. In this entry, I create the user interface for the Forums application by taking advantage of ASP.NET AJAX client templates. I build the entire user interface for the Forums application with a single view and perform all updates to the view with Ajax calls.

A page request is just an excuse for a bad user experience. We are conditioned to expect our web pages to freeze whenever we interact with a website. For example, each and every time we submit a form, we expect the browser to freeze and the clock to tick until the browser updates the page again.

There is no reason to inflict this pain on the users of your web applications. By taking advantage of Ajax, you can create web applications that respond instantly to user interactions. In this blog entry, I demonstrate how you can take advantage of Ajax when building ASP.NET MVC applications.

In particular, I discuss how you can take advantage of a new feature of ASP.NET AJAX arriving with the next version of this technology. I discuss how you can take advantage of ASP.NET AJAX client templates in the context of an ASP.NET MVC application. A preview version of client templates is available now and you can start experimenting with this technology in the applications that you are building today.

In this blog entry, I demonstrate how you can create a single page ASP.NET MVC application. Single page web applications are the holy grail of the Ajax developer. In a single page application, only one page must be requested by the browser. After the single page has loaded, all updates to the page are performed using client-side JavaScript. All new data displayed in the page is retrieved by performing Ajax calls against the server.

An Ajax application takes a little more work to write (yes, you do have to write some JavaScript), but it provides your users with a much better user experience.

The Single Page

The single page (the single view) in our MVC Forums application is contained in Listing 1. Notice that the header of the view contains several JavaScript includes. The view takes advantage of the following JavaScript libraries:

· MicrosoftAjax.js – The main Microsoft AJAX Library. Required for all things AJAX related in the Microsoft universe.

· MicrosoftAjaxTemplates.js – The preview version of the client template library included with the next version of Microsoft AJAX.

· MicrosoftMvcAjax.js – Extensions to the Microsoft AJAX Library for the MVC framework. This script is included with the default MVC Visual Studio project template.

· MvcAjax.js – A JavaScript Library that I wrote which contains useful helper methods for invoking controller actions.

· MvcForums.js – A JavaScript library that contains all of the user-interface logic used by the MVC Forums application.

Listing 1 – ViewsForumsIndex.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcForums.Views.Forum.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>MVC Forums</title>
    <link href="../../Content/MvcForums.css" rel="stylesheet" type="text/css" />
    <script src="../../Content/MicrosoftAjax.debug.js" type="text/javascript"></script>
    <script src="../../Content/MicrosoftAjaxTemplates.debug.js" type="text/javascript"></script>
    <script src="../../Content/MicrosoftMvcAjax.debug.js" type="text/javascript"></script>
    <script src="../../Content/MvcAjax.js" type="text/javascript"></script>
    <script src="../../Content/MvcForums.js" type="text/javascript"></script>
</head>
<body>
    <div id="banner" class="panel">
        <h1>MVC Forums</h1>
    </div>

    <div id="main">
        <div id="threadsAndMessageContainer">
            <% Html.RenderPartial("~/Partials/Threads.ascx"); %>
            <% Html.RenderPartial("~/Partials/Message.ascx"); %>
        </div>
    <% Html.RenderPartial("~/Partials/Post.ascx"); %>
    <% Html.RenderPartial("~/Partials/Reply.ascx"); %>
    <% Html.RenderPartial("~/Partials/LoginRegister.ascx"); %>
    </div>

    <script type="text/javascript">
        Sys.Application.initialize();
    </script>
</body>
</html>

Notice, furthermore, that the body of the view in Listing 1 consists of a series of calls to the Html.RenderPartial() method. The MVC Forums application contains several forms: a login form, a registration form, a post form, a reply form. Each of these forms is contained in a separate partial. Placing the different forms displayed by the single page Ajax application in different partials makes it easier to manage the application. For example, the partial used to post a new message, named Post.ascx, is contained in Listing 2.

Listing 2 – PartialsPost.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Post.ascx.cs" Inherits="MvcForums.Partials.Post" %>
<form id="post" action="" class="panel">

<label for="post.Subject">Subject:</label>
<br />
<input name="post.Subject" />

<br /><br />
<label for="post.Body">Body:</label>
<br />
<textarea name="post.Body" cols="60" rows="8"></textarea>

<br /><br />
<button id="post.Post">Post</button>
<button id="post.Cancel">Cancel</button>


</form>

There is nothing special about the partial in Listing 2. It simply consists of a form for posting a new message to the MVC Forums.

Using Client Templates

By taking advantage of client templates, you can format any array of JavaScript objects. Client templates are included with the next version of the Microsoft AJAX Library. You can download a preview of client templates from the following address:

http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16766

The MVC Forums application uses client templates in two places. First, a client template is used to format the list of message threads (see Figure 1). The list of threads is retrieved from the server with an Ajax call. When the data is returned, the data is formatted with the help of a client template.

Figure 1 – Message threads are rendered with a client template

image

Second, a client template is used to format a particular thread (see Figure 2). A thread consists of a post and all of the replies to the post. The contents of a thread are also retrieved with an Ajax call from the server.

Figure 2 – A particular thread is rendered with a client template

image

A client template is a fragment of XHTML. The client template used for formatting the list of threads is contained in Listing 3.

Listing 3 – allThreads template

<ul id="allThreads" class="sys-template">
<li>
        {{Author}} &mdash;
        <a href="" messageid="{{Id}}">{{Subject}}</a>
</li>
</ul>

There are two things that you should notice about the template in Listing 3. First, notice that the opening tag for the template includes a CSS class attribute that points to the sys-template class. This class is defined in the Forums.css file. The sys-template CSS class sets the display attribute to the value None in order to hide the contents of the template.

Second, notice the expressions {{Author}} and {{Subject}}. These expressions are replaced automatically with the values of these properties. These expressions work very much like the <%# Eval() %> expressions in a server-side ASP.NET page.

The template in Listing 3 is used by a client-side AJAX DataView control. This control is instantiated with the following line of code in the MvcForums.js library:

_allThreadsView = $create(Sys.UI.DataView, {}, {}, {}, $get(“allThreads”));

This line of code creates a new client-side DataView control named _allThreadsView that uses the template in Listing 3. After you create a DataView control, you can assign data to the control by using its set_data() property. You can assign any JavaScript array that you want to the DataView by calling set_data(). In the MvcForums application, the array of data is retrieved from the server through an Ajax call which I describe in the next section.

Actions are Invoked with Ajax

The MVC Forums application never performs a normal post to the server. All information is conveyed back and forth between the Index view and the server through Ajax calls. The Ajax calls invoke MVC controller actions asynchronously.

In order to invoke MVC controller actions asynchronously, I had to write some helper methods. The MvcAjax.js file contains the following two helper methods:

· MvcAjax.MvcHelpers.invokeGet(url, succeededCallback, failedCallback)

· MvcAjax.MvcHelpers.invokePost(url, form, succeededCallback, failedCallback)

The first method enables you to invoke MVC controller actions by performing an HTTP GET. The second method enables you to invoke MVC controller actions by performing an HTTP POST.

For example, the list of message threads is retrieved from the following action exposed by the Forum controller:

public JsonResult Threads()
{
    return Json(_repository.SelectThreads());
}

This controller action returns a JSON result that represents all of the message threads. The list of threads is retrieved from the ForumRepository.

The following JavaScript code is used to display the threads:

MvcAjax.MvcHelpers.invokeGet("Forum/Threads", updateAllThreads);

function updateAllThreads(data)
{
    _allThreadsView.set_data(data);
}

The invokeGet() method invokes the Forum controller Threads() action. When this action returns the JSON result, the JSON result is passed to the updateAllThreads() method which assigns the data to the _allThreadsView client-side DataView control. At this point, the data is formatted by the client template and the list of threads is displayed.

Network Traffic is Kept to a Minimum

By taking advantage of client templates, you can reduce your network traffic to the bare minimum necessary to transfer database records between the server and the client. Unlike an UpdatePanel, when you use a client template, you don’t need to ship actual HTML markup between the server and client. The only thing that gets passed between server and client is pure data.

Furthermore, because we are working in the context of an MVC application, there is no view data that needs to be passed between server and client. Because we are building a single page application, there is really no reason for view data to exist. View data enables you to preserve state across postbacks. However, we don’t ever lose any state because we never perform a postback.

Passing only essential information between the server and client improves the responsiveness of the Forums application. The entire page is not reloaded whenever you click a new message thread to view it. Instead, only the message thread itself is transferred across the wire.

Forms Appear Instantly

When you click the New Post link in the MVC Forums application, the form for posting a new message appears instantly (see Figure 3). Unlike a normal web application, the form is not retrieved from the server using a page request. Instead, the Post form is loaded when the first page is loaded. It is hidden (with display:none) until you need to post a new message.

All of the forms in the MVC Forums application work in the same way. The Login, Register, Reply, and Post forms are all shipped to the browser with the first page request. You can post and reply to messages very quickly because these forms do not need to be repeatedly requested from the server.

Figure 3 – Posting a new message

image

Summary

I created the user interface for the Forums application that I describe in this blog entry as an experiment to test the new client templates functionality coming with the next version of the Microsoft AJAX Library. I discovered that client templates work very well in the context of an ASP.NET MVC application.

ASP.NET MVC enables you to easily build actions that return JSON results. Client-templates enable you to easily format JSON results. ASP.NET MVC and ASP.NET AJAX client templates are the perfect combination for Ajax fanatics like me.

I recommend that you download and play with the MVC Forums application included at the end of this blog entry. I think that you will be surprised by the responsiveness of the application.

Download the Code

Discussion

  1. http:// says:

    It is better if using jQuery.

  2. http:// says:

    Hi, Stephen,

    Thank you for addressing an interesting topic of single page Ajax applications. 🙂

    I haven’t played yet with the MVC, but…. I’m just curious, with ASP.NET MVC, wherever updates occur, what happens in the address bar? Is the currently displayed information item somehow reflected in the Url or does the address always stay the same?

    Actually, this issue with never changing address with ASP.NET AJAX (not MVC) caused be to abandon the idea of the “holy grail”-type single page application. As you know, search engines will only see the initial single page on such a site, therefore threads and posts will never get discovered and indexed. 🙁

    Is this the same with MVC AJAX model?

  3. How about the browser’s history?

  4. ricky says:

    This is a nice tool, but I am afraid it will be misused and perhaps be a crutch for those not wanting to get down and dirty with the coolness of JavaScript… SEO and Accessibility (or lack thereof) can be a big issue if this is used too much and in the wrong ways…

    Also, in the paragraph “…..View data enables you to preserve state across postbacks…” I assume you mean ViewState, correct?

    Thanks for the Tips!

  5. http:// says:

    hi,
    i used mvc in php, but i dont know it in asp.net before i read this blog. thanks very much u made me to know now… its very helpful to me…

  6. http:// says:

    I’m receiving the following error:
    Webpage Script Errors

    User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
    Timestamp: Tue, 23 Sep 2008 23:02:17 UTC

    Message: Syntax error
    Line: 52
    Char: 13
    Code: 0
    URI: http://localhost:62203/Content/MvcAjax.js

  7. http:// says:

    Hi Stephen,

    I’m following your series exploring MVC. You have been very good in explaining core features in all of your blogs. I have one concern with this series as I’m following along and that is the MvcFakes project codebase does not seem to exist anywhere except for Tip #29. It is referenced in subsequent tips (#33 and #34) before Forums #2 and it is missing the IDataContext interface that is used in Forums #2.

    I’d like to suggest that either the MvcFakes project be included as a snapshot as it exists for each of your segments or at least upload the project to codeplex so that we may follow the MvcFakes project as it progresses there.

    Aside from that Good Job!!

    ~-=Mike=-~

  8. Great Tips! Thanks for sharing very useful tips.

  9. Thanks for sharing very significant blog.

  10. RW W Great article, though – thanks!

  11. Building forums through ASP.net is so hard. Thank you for sharing the this code.

  12. 21 Thank you for sharing the this code.

  13. wow.. interesting info at this post thanks!!!

  14. Sayeed Ahmed says:

    I very new on MVC . I am trying develop a page which has text box and submit button. I have tested my page with Ajax submit and its working fine. But I want to display my product list when i submit the button . Its not refreshing my page . I have debug my controller and found that view return product list but its not showing on browser.
    Can you please tell what would be the problem.

    –code Index page
    < %@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage>” %>


    List of Products

    List of Products




    < % using (Ajax.BeginForm("ProductbycatagoryID", new AjaxOptions { UpdateTargetId = "results" }))
    { %>

    < %= Html.TextBox("query")%>

    < %Html.RenderPartial("ctlProductSearch", ViewData.Model); %>

    < %= Html.ActionLink("Create New", "Create")%>

    < %} %>

    ————-
    controller code—
    ——

    public ActionResult ProductbycatagoryID(string query)
    {

    //return Content(“You entered : ” + query);

    var products = productrepository.GetAllProductbyCatagoryID(Convert.ToInt32(query));
    return View(products);
    }

    —-cltProductsearch–
    < %@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl>” %>

    < % foreach (var item in Model) { %>

    < % } %>

    ProductName Description SortDesc Price Picture Discontinue Rank SpecialItem
    < %= Html.Encode(item.ProductName) %> < %= Html.Encode(item.Description) %> < %= Html.Encode(item.SortDesc) %> < %= Html.Encode(String.Format("{0:F}", item.Price)) %> < %= Html.Encode(item.Picture) %> < %= Html.Encode(item.Discontinue) %> < %= Html.Encode(item.Rank) %> < %= Html.Encode(item.SpecialItem) %>
  15. Smith says:

    he Coyote walking across the shot was early morning, and he was as surprised to see me as I was to see him Psychology Diploma | Bachelor degrees & hr diploma

  16. Smith says:

    Mishka! I appreciate you all taking the time to sign in and comment on this show. What you don’t see in the show is me freezing my tail off for these computer degree | online doctorate degree

  17. Thanks for your information, i have read it, very good!

  18. Thank you for sharing the this code Stephen

  19. 4lo
    I believe it is a promising (currently version 4.0). So I would stick with it.. thanks