Easy AJAX with MonoRail

AJAX gives your web applications a better user experience.  They'll get quicker feedback on their actions and it won't require a full page refresh.  In this post, I'm going to review the basics of doing AJAX in MonoRail.  A complete Visual Studio 2008 solution for this sample project is available from my Google Code page.

Scenario

For this demo, we'll have a dropdown listbox that allows the user to select a number.  After selecting the number, we'll update an area of the web page with dynamically generated content and this will all be done without doing a full page refresh.

Setup

Like many MVC frameworks, MonoRail allows you to define a common layout for your website (think Master Pages in webforms).  My layout for this sample is simple (I'm using the NVelocity view engine):

<html>
<head>
    <title>MonoRail Sample Application</title>
    <link rel="stylesheet" href="$siteroot/Content/css/base.css" />
    $AjaxHelper.InstallScripts()
    $!head
</head>
<body>
    $childContent
</body>
</html>

MonoRail has built-in AJAX support via the prototype framework. The "$AjaxHelper.InstallScripts()" emits the proper javascript tags to include the prototype scripts for use in our application.  You're free to choose a different AJAX library if you'd like, but the built-in "AjaxHelper" class makes using AJAX from MonoRail a breeze.  I also have a placeholder ($!head) so that individual pages can add additional code to the <head> tag (like page-specific Javascript).

Some people may ask, "If you install the prototype library in the layout, doesn't it get included in every page even if you don't need it?"  Yes, it does.  If you'd like to limit where your AJAX is available, move the $AjaxHelper.InstallScripts() into each individual page as needed.

The Controller

Let's create a simple MonoRail controller for our AJAX sample.  For right now, we'll define the "Index" action as the action that is the beginning of this app:

[Layout("default")]
public class AjaxSampleController : SmartDispatcherController
{
    public void Index()
    {
        PropertyBag["counts"] = Enumerable.Range(1, 10);
    }
}

Nothing fancy.  We place a range of numbers (1-10) in the PropertyBag and we'll use that to generate our combo box.

The View

We'll keep the view simple so we can focus on concepts.  Something like this will do:

image

We'll implement the above in our view:

<div>
Select the number of items to display: $Form.Select("count", $counts, "%{onchange='DoAjax()'}")
</div>
<br />

Now we've got a dropdown list and we've hooked up the onchange event to call "DoAjax()" -- we'll discuss this later.

Now comes the part of the page for our AJAX update.  We'll define a <div> tag to hold the part of our screen we want to dynamically update.  Further, we'll define a separate view just for rendering the AJAX portion of the div.  Heres' the entire view for the "Index" method:

<div>
Select the number of items to display: $Form.Select("count", $counts, "%{onchange='DoAjax()'}")
</div>
<br />
<div id="ajaxarea" style="border: solid 1px blue; background-color: #cccccc">
#parse("ajaxview.vm")
</div>

I placed a border around the <div> and changed the background color to help differentiate it from the rest of the page.  What do we put in "ajaxview.vm"?  Whatever we want to!  Here's ajaxview.vm.  It looks for the existence of a "selectedCount" variable and loops to create the <p> tags:

#if($selectedCount)
    #foreach($i in [1..$selectedCount])
        <p>item: $i</p>
    #end
#end

Client-side AJAX Code

Now we'll add the Javascript necessary to implement the DoAjax() function:

#capturefor(head)
<script language="javascript">
    function DoAjax()
    {
        var items = $('count').selectedIndex + 1;
        new Ajax.Request('$UrlHelper.For("%{action='GenerateCounts'}")',
            {
                method: 'get',
                parameters: {
                    count: items
                },
                onSuccess: updateAjaxArea,
                onFailure: showMessage
            });
    }
    
    function updateAjaxArea(transport)
    {
        $('ajaxarea').innerHTML = transport.responseText;
    }
    
    function showMessage(transport)
    {
        alert('An error occurred during the AJAX request.');
    }
</script>
#end

First off, we use MonoRails "capturefor" component.  This takes everything between the "#capturefor" and the "#end" and assigns it to the "head" variable.  As you remember, the "head" variable was defined in the layout and therefore, all of this javascript code get's put in the proper place in our HTML page (i.e. inside the <head> tag).

The first thing DoAjax does it determine the item selected in the dropdown list.  Next, it creates an Ajax.Request object that calls the "GenerateCounts" method of our controller.  The "count" parameter is set to the value selected in the dropdown.  Finally we set the "onSuccess" and "onFailure" callbacks.  As you might have guessed, one of these will be called depending on the success or failure of our AJAX call.

The update callback takes the responseText of our AJAX request and updates our <div> tag we set up earlier.  The prototype documentation contains a full description of all of the properties and methods exposed by the transport object in the callbacks.

Server-side AJAX Code

The controller code for our method is even simpler that our client-side code:

public void GenerateCounts(int count)
{
    PropertyBag["selectedCount"] = count;
    RenderView("ajaxview");
}

All we do is take the value passed in (the value selected in the dropdown list) and place it in the PropertyBag as "selectedCount" (NOTE: MonoRail's SmartDispatcherController takes care of pulling the "count" from the HTTP request and assigning it to the "count" parameter of the method).  Then we tell MonoRail that it should render the view "ajaxview".  Remember that this view simply loops and creates <P> tags based on the value of "selectedCount".

Success!

That's it!  That's all there is to it.  Run the sample code and select a value from the dropdown list.  You'll notice that the area under the dropdown is updated as you pick different values and there is no full page refresh.

The complete VS2008 solution for this project can be downloaded in ZIP form or you can access the SVN code.

Conclusion

This exmaple just showed the mechanics of making AJAX requests via MonoRail's built-in AJAX support.  You can see how something like this can easily be used to render an entire <TABLE> from a database query or do some other complex logic -- all without a full page refresh.  If you have any questions, please don't hesitate to contact me.

Technorati tags: , ,

2 Comments

Comments have been disabled for this content.