jQuery for Asp.net MVC preview 3

by Chris van de Steeg. 11 Comments

So, I finally got a chance to sit down and finish up the jQuery for Asp.net Mvc preview 3. If you haven’t already read Scott’s post about preview 3,  you should definitely do so before reading this one.

So, quite some things have changed in preview 3, which made me decide to change a lot in the jQuery for Asp.net mvc (jqmvc) too. To start with most radical one: I ditched the ajax.master. This little ‘framework’ does not rely on a special masterpage in your website from now on. Since things have radically changed, I’ll start all over again on how to use this project.

In this post, I’ll explain how the project works globally. I won’t go into too much details about everything, but I will just explain the things you’ll directly touch in your application. In the next post, I’ll explain how to use the project in your website (until that time you could take a look at the included sample website). That post will explain the Ajax- and HtmlHelpers in this project and it will explain how to do an ajax request using jQuery.

Before my story starts, let’s start with the links and downloads for all those who want to read code rather than stories :

Application and routes

To have a quick go for your application, it’s recommended that you inherit your application from JqueryMvc.JqueryMvcApplication. You can ofcourse easily do this in your global.asax like so:
image
This application sets up the Unity dependency container and will set up the default routes. If you wish to setup a different (or no) dependency container, you should override InitializeContainer() and ConfigureContainer(). JQueryMvc itself is no longer dependent on a dependency container.

I found it hard to set up the default routes in preview 3 if your server does not support wildcard redirects (like visual studio’s webdevserver doesn’t). In that case you need to catch existing files (default.aspx needs to exist, but shouldn’t do anything), but you need to skip the Content folder. I came up with the following solution:
image
So if there’s a default.aspx in the root of your application, it set’s routeExistingFiles to true. If the url matches the RegEx ^Content\/.* the routing will be stopped using the new IgnoreRoute method. Also, your controllers will then be extended with .aspx to be sure the requests passes the asp.net cycle. If you have the option to catch all incoming requests, all you have to do is remove the default.aspx from the root of your website.

Controllers

As you’ve seen in Scott’s post, controllers should now return an ActionResult. The normal way would be to have your controller action return the result of the View() method. With jqmvc you should not call the View() method at the end of your action. Ofcourse, if you’re sure you want to return the normal view, you could still do so. In jqmvc there is a ExtController which you should inherit your controllers from. This baseclass contains a method called Action() which accepts the same attributes as View() but does some magic to decide what type of request the current request is, and will try to return the correct ActionResult for that type of request. This could be either an Ajax-request, a Json request or a normal page-request. Your typical controller with some actions would look like this:

   image

Views

If jqmvc encountered the current request as a normal page request, it will create the default ViewResult, nothing special there.

If jqmvc decided Json should be returned, a JsonResult is created and no further actions will be taken. The rendered view will be the ViewData serialized to Json. The ViewEngine of the controller is also changed to a JsonViewEngine but this is only called upon in case of an Exception.

If jqmvc decided that the request is an Ajax request, it will create a normal ViewResult, but it will change the ViewLocator of the controller.
(Offtopic: I think it’s a design-error to have the viewlocator on the controller. It should be on the ViewResult object, that’s the only object that uses it. Perhaps this will be changed in the next preview…)
This AjaxViewLocator will first look for .ascx files instead of .aspx files. If it can’t find the .ascx it will load .aspx afterall.  You can also create a target-specific .ascx file: if you do an ajax-request through our javascript function jQuery.mvc.request() (or one of the htmlhelper functions), you have to specify a target for the loaded content. Let’s say we want to load /Home/Index into a <div id="dynamiccontent"></div>
The AjaxViewLocator will then look for :

  1. /Views/Home/Index.dynamiccontent.ascx
  2. /Views/Home/Index.acx
  3. /Views/Home/dynamiccontent.ascx
  4. /Views/Home/Index.aspx
  5. /Views/Shared/Index.dynamiccontent.ascx
  6. /Views/Shared/Index.ascx
  7. /Views/Shared/dynamiccontent.ascx
  8. /Views/Shared/Index.aspx

To have the page /Home/index available both as an ajax request as well as normal request, you could create an /Views/Home/Index.aspx which loads the index.ascx like so:image

Things to know

  • We have some HelperClasses to simplify the use of jqmvc

  • The ViewData keys "Messages" and "Errors" are treated specially by jqmvc.

  • Your page should always contain:

    • <%= Html.RegisterJqueryMvc(Page.ClientScript) %> To register the required javascript files

    • <div id="loading"></div>; or something else with id="loading". This will be made visible during ajax-requests

    • <div id="errors"></div> to display errors

    • <div id="messages"></div> to display messages (informational)

  • We have a special contentplaceholder in JqueryMvc.UI.WebControls.ContentPlaceHolder. This will use the AjaxViewLocator to load some default content. Eg, if used like this in your masterpage, and the current request is /Home/Index:
    <jq:ContentPlaceHolder ID="maincontent" runat="server" />
    It will look for for content in:

    1. /Views/Home/Index.maincontent.ascx
    2. /Views/Home/Index.acx
    3. /Views/Home/maincontent.ascx
    4. /Views/Home/Index.aspx
    5. /Views/Shared/Index.maincontent.ascx
    6. /Views/Shared/Index.ascx
    7. /Views/Shared/maincontent.ascx
    8. /Views/Shared/Index.aspx

      This way, your index.aspx could just be an empty page that points to MasterPage with this default-content-loading contentplaceholer.image

  • Your ascx files should inherit from JqueryMvc.Mvc.ExtViewUserControl to have access to the AjaxHelper (bug in asp.net mvc!) and to have the messages & errors parsed by jqmvc.
  • In case of an error the default controller will look for a rescue page in /Views/[ControllerName]/Rescues/error.aspx or /Views/Shared/Rescues/error.aspx (or .ascx in case of an ajax request). You can specify your own rescues for different types of errors by using the RescueAttribute of
  • You should remove default.aspx if your application-server supports wildcard mappings.

11 Responses to jQuery for Asp.net MVC preview 3

  1. Jake Scott says:

    Hi Chris, I used the same technique when preview 3 came out and it works really well.

    I have a small suggestion and that would be to use the rev attribute of the a tag to define the target instead of the meta:ajaxtarget=”#maincontent”. Its just that it makes development so much easier when developing your pages with green validation lights :)

    http://reference.sitepoint.com/html/a/rev

    Does the ajax history stuff you included rely on this attribute?

  2. Chris van de Steeg says:

    @Jake: the meta:ajaxtarget is used to determine where to put the loaded ajax-content (which div). I used this so it would be easy to create your own anchor-tags without having to using the HtmlHelpers.

    I agree, it should be valid xhtml. I’m not yet sure if using the rev attribute would be the best way to go though. We’d be using it the wrong way then, since it should indicate a relation between the current and the next page.

  3. Nick Berardi says:

    Hi Chris, I like where this is going. I just have a few concerns about why you require a Controller to be inherited. You can accomplish the same results by using an Attribute. I have created an attribute that allows you to simple specify [Service] at the method or controller level that will automatically serialize the ViewData on output if JSON, JSONP, or XML is requested.

    http://code.google.com/p/coderjournal/source/browse/trunk/ManagedFusion/Source/Web/Mvc/ServiceAttribute.cs

    I also created my own serializer that works with anonymous types. I was having trouble with the JavaScriptSerializer that the Json ActionResult uses.

    contact me if you have any troubles.

  4. Chris van de Steeg says:

    @Nick: Excellent suggestion! I’m converting it right now. Thanks alot

  5. Roger Chapman says:

    You mentioned ignoring the Contents folder in your routes; this is not required.
    In the new routing engine, if the file being requested exists on the folder structure (as the Contents folder) it will be automatically ignored by the routing engine.

  6. Chris van de Steeg says:

    @Roger: that’s only the case if you have the option to do catch all incoming requests (wildcard mapping). If you need .aspx in your url to go to asp.net (eg because your hosting company requires you), you need to exclude the Contents folder yourself

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>