ASP.NET MVC Beta – Ajax

I was noticing the asp.net mvc walkthrough has a section on the Beta1 ajax using the Microsoft ajax library.  Although I’m a big fan of jQuery, these two libraries now come with the framework and can be used together quite well.

In the walkthrough the code samples show a simple call to a controller which returns a string.  Let’s take that a step forward and show how it can return different types using the ‘ActionResult’.

First, make sure you include the scripts in your page – I suggest including them in the head of the master page – in the sample this is under Shared/Site.Master.

<script src="<%= Url.Content("~/Scripts/MicrosoftAjax.debug.js") %>"
        type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/MicrosoftMvcAjax.debug.js") %>"
        type="text/javascript"></script>

 

(note: in the walkthrough, they are a bit outdated, it should now point to the Scripts folder, not the Content folder – which is what is recommended, as well as a part of the project template install)

Here is the sample – they have added this to the HomeController:

public string GetStatus()
{
    return "Status OK at " + DateTime.Now.ToLongTimeString();
}

public string UpdateForm(string textBox1) { if (textBox1 != "Enter text") { return "You entered: \"" + textBox1.ToString() + "\" at " + DateTime.Now.ToLongTimeString(); }

return String.Empty; }
 

In step one of this, I’m going to go ahead and make a change – where I prefer to return an ActionResult here.

Since it’s just returning a string, we can return using ‘Content’. Here is an example:

 

image

Adding Ajax Helpers to The View

The Mvc framework comes with several new Ajax helpers, the two I’m going to use here from the walkthrough  is the Ajax.ActionLink and the Ajax.BeginForm.  There are named identical to the Html.ActionLink and Html.BeginForm (previously this was called ‘Form’ – now in Beta1 it’s BeginForm)

Let’s look at the ActionLink:

<%= Ajax.ActionLink(“Update Status”, “GetStatus”, new AjaxOptions{UpdateTargetId=”status” }) %>

By default, since the the container view is the ‘Home/Index’, it will look for the Action on the HomeController called ‘GetStatus’.  In the AjaxOptions – it is defining the target for the return of the GetStatus action. In this sample it is a span  with id of ‘status’ : <span id=”status”>No Status</span>  This can be a span, div, etc…  Since we have told ‘GetStatus’ above to return ‘Content’ – it will return a string and place the result in the span element.

I prefer the syntax for the helpers that is included in the Microsoft.  You’ll need to get this ‘futures’ assembly separately (here – you can learn more about it at the bottom of Scott Guthrie’s blog post on the beta1) and include it in the references as well as add a the following to the ‘namespace’ section of the web.config:

<add namespace=”Microsoft.Web.Mvc”/>

(personally I wish they would have just included it in the install… but you know how it is… lol)

So, for some reason they didn’t include the same syntax for the Ajax helpers as they did the Html helpers, the strongly typed ActionLink, ie. <%= Html.ActionLink<HomeController>(c => c.GetStatus(), “Update Status”)%>

This is preferred by me, hopefully they will include it, if not… well, I’ll make another post on how to build your own  🙂

Let’s get back on track.

When we run the application, we will see the following:

image

Clicking ‘Update Status’ makes a call back to the Action – asynchronously:

image

What is happening is that the helper control is creating the link in the html using the asp.net ajax library, looking at the source, you’ll see the following:

<a href=”/Home/GetStatus” onclick=”Sys.Mvc.AsyncHyperlink.handleClick(this,

new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace,

updateTargetId: ‘status’ });”>Update Status</a>

 

Let’s take a brief look at the code here and how we can include some events to the Ajax.ActionLink,

the screenshot below shows the available ‘AjaxOptions’:

image 

Let’s add some functionality and handle the 2 most common events, the ‘OnBegin’ and ‘OnSuccess’:

 
image
 
 

The ‘OnBegin’ is going to handle the event fired before the actual ajax call is made, and the ‘OnSuccess’

will let us handle when the call is done (note: always handle the ‘OnFailure’ as well, in case of any errors!)

In my example I’m adding ‘OnRequest’ and ‘OnResponse’ – these are javascript functions, let’s look here:

 
image 

When the link is clicked, an alert box will open, when it’s done, you’ll get a second alert box. As you might notice I’ve included the ‘content’ in the OnResponse. Using Firebug, we can look at the object in detail:

 
image
 

We can see the xmlHttpRequest object and it’s request to the action ‘GetStatus’. I’m not going to go into more detail into the asp.net ajax library, but you can read more here

I should note, a good way to handle your own validation in javascript is to make validation calls in the ‘OnBegin’ – and if you want to stop the ajax call from continuing, you can return false.

So, let’s go ahead and show a similiar example with a Ajax.BeginForm, as with above, I’m converting the sample that returns a string, to return an ActionResult, using return Content(…):

image

In the view, I’m using the Ajax.BeginForm – (note that I’m wrapping it in a using statement, which I consider to be the best practice):

image

I’ve included handlers for the OnComplete, OnBeing, and OnFailure:

image

(I did manage to sneak in some jQuery goodness, using the $(“#textBox1”) to get the textBox1 element’s value)

Let’s make a minor adjustment here!  Let’s say I want to return some content, outside of ‘just a string’ ?  What I do on my project is have the ActionResult return a control.  Let’s set this up for our grand finale:

Step One:

Create a new Ajax.ActionLink: (you can do this with a form)

image

Step Two:

Create the action on the controller (home):

image

Step Three:

Create a ‘MVC View User Control’ (ascx):

image

image

Step Four:

Add some content to the control:

image

As I show, you can add a model to this control, etc… if you want to see that let me know.

Now, let’s run this add see what our result is:

We added the link at the bottom:

image

When it get’s clicked, it makes an asynchronous call, and then updating just the ‘status’ span (partial page update):

image

This is, imo, much more efficient and clean, then let’s say an ‘UpdatePanel’ in Webforms.  I have full control over the page.  The addition of the asp.net ajax libraries with ajax helpers really helps make this simple to use but extremely powerful.

Example available

Advertisements

35 thoughts on “ASP.NET MVC Beta – Ajax

  1. Actually, in Beta 1 they removed the strongly typed Html.ActionLink as well. The reason is that you have “no way of knowing” what name an action might end up answering to.

    Thinks like filters and ActionName attribute can alter what name an action method eventuelly ends up answering to, so your Index action might be decorated with a ActionName attribute which renames it

    Phil Haack mentions this in the discussion section for his posts on how a mehod becomes an action

    http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx

  2. I recall that now. So in order to allow for altering the name of an action they remove the strongly typed calls.

    I’m not sure I think this is such a great idea.

    Maybe that just concerns me because it’s on feature I really liked 🙂

  3. I *really* liked the syntax using the Lambda expression and my initial reaction was “aw crap!” but Phil makes a convincing argument *hehe*

  4. Hi Steve,

    In the past preview versions was a AjaxHelper included which helpd to initialize the recommended scripts etc.
    Ajax.Initialize(); Ajax.RegisterScript(); Ajax.Render();

    Is there anymore such a helper on bord? Or is the best solution to add some lines with all the jQuery and MicrosoftAjax scripts to the master page?

  5. I’ve personally added my references in the master pages using the src=Url.Content(~/scripts/…) syntax.

    One advantage for me with this is it makes it easier to manage what is being used in one location.

    The only downside is that if I have a page that isn’t using any of these references, it’s adding all that to the page. To help with that, as well as to give flexibility I also create a content tag in my head section on my master page after the includes.

    With that, I can have a unique references to scripts on any given page. ie. if I have a page with charting and need to include a jquery charting script – it can be added just to that page and yet still be included in the header.

  6. ok – so this works – however I need to reattach some events after the partial rendering is complete….instead, I get the script executed right before the rendering starts at the end of the Ajax call. Is there anyway to add a $(document).ready like functionality to the update target?

    In my specific example – I am using the JQuery UI tabs and all the event binding is gone after the Ajax form is submitted.

  7. If I’m not mistaken, the MS Ajax library has a ‘PageLoad’ event – you could try using that in the same manner as the jQuery ready:

    ie.
    function PageLoad()
    {
    //add this to the update target
    }

  8. Thanks for your example! Using a user control to update content is great 🙂

    I just have one problem…

    I have two regions I want to update simultaneously (two lists, items in one list can’t be in other). The update works great on the list I modify, but I can’t figure out how to throw in some AJAX so that the other list gets updated.

    Basically, I move items between the lists.

  9. Jessica, you can make this that you want with Jquery, it’s very simple, you just have to make a scrip that update the another panel if one is reloaded.

  10. Hi Steve. Thanks for the example, it was very usefu! I have a question: How can I pass a parameter value when using a Ajax.ActionLink? Thanks!

  11. One of the overloads of ActionLink is is ‘RouteValueDictionary’ – that is one way.

    This code produces the following:
    a href=”/VirtualDirectory/YourController.mvc/Person?LastName=Nascentes”>LinkText

    (I had to remove some of the tags to make it work in this editor)

    To add multiple parameters it would look like this: new {FirstName=”Andre”, LastName=”Nascentes”}

    However, I prefer to use the Generic ActionLink with the expression – it removes the ‘magic strings’, etc… and with the new RC, has compile time checking:

    <%= Html.ActionLink(c => c.Person("Andre", "Nascentes"), "LinkTest") %>

    This second example assumes the following action syntax in your controller:
    public class YourController

    public ActionResult Person(string firstName, string lastName)

    What is better about this second approach is that if the signature changes, the compiler will catch the change of the signature (assuming you have enabled this in your asp.net rc project file)

    I hope that is clear

    (EDIT: I see your talking about ‘Ajax.ActionLink’ – it also has the same RouteValueDictionary, so it would work the same way as above example)

  12. Hi, I am facing an issue with MVC, Can you please suggest a work-around?

    I have a partial view that is rendered at …mysite/Home/Index and there is another button on the page which opens a pop up(modal) that contains this control again… now in this pop I modify the view model of this partial view and on close it is updated, the view model has been updated but the HTML on the page has not, I have tried JQuery and Ajax approaches to avoid page refresh/reload as I have unsaved data on the page but niether updated the HTML(seen on view page source as I need to perform edit operation on the appended HTML).

    Thanks in advance

    Best Regards

    KK

  13. If you don’t end up reloading the underlying page to get the updated results, you would have to start working on the client – ie. modifying the results on the client. That changes the model however, ie. you might end up pulling down json, updating the json, etc…

    Otherwise, you end up doing as you say, making a call to save, close the dialog, then reloading the underlying page to get fresh results. I have done both. With the later, I ended up using BlockUI to handle showing the status ‘Updating’ then after the update, ‘Refreshing Data’.

    Without some client side work that updates the underlying page, your going to need a reload of elements in your example

  14. Thanks Steve,

    What I have done is appended the changed HTML to the place(div) where the changes are to be portrayed, the appearance is fine but the only issue is that the changes are not reflected on the page’s HTML(which I see when I see the View Source) this is leading to an issue with row edit and delete operations as the control is not able to find the ID’s of the newly appended rows, I have done similar stuff with ASP.Net AJAX using update panels and triggers but MVC seems to have a different AJAX approach, and more over the AJAX Trigger(Submit button of a form) cannot be programmatically triggered.

    My current approach does not use JSON but if there is not another solution to this the I shall go 4 JSON.. but in that case would the reload on a partial view be possible?

  15. Also the case of reloading the underlying page is out of the question as there would be unsaved data on the page, I just need to reload the partial view that I am updating the data to!

    Thanks a ton!!

    KK

    1. Hmmm, You might not see it in view source, but these items should be in the DOM itself. Make sure the ids are unique, your row editing ‘should’ work ?

      If you use Firebug or IE developer tools (or chrome dev tools) you should be able to see DOM updates.

      ie. I just added elements to a page in a test, opened up chrome dev tools and could select the new html to see it’s details

  16. Hi Steve,

    Thanks for the update yes you are right I am able to see the updated id’s through firebug…

    The problem is still occurring but I have the two HTML’s now can you tell what am I doing wrong?
    Working:

    delete
    Not Working:
    delete

    The Jquery I am using is:
    $(“button[id^=’Delete’]”).click(function(event) {
    $(this).parents(“li[id^=’PageliInDiv_’]”).animate({ opacity: “hide” }, “slow”);
    //Update View Model??

    });

    The HTML of the part to be deleted is as follows:
    Working:

    $1 – $100

    delete

    edit

    Not Working:
    $2501 – $3000deleteedit

    Thanks a ton…

  17. The Id’s are working fine but the JQuery for the newly appended Items is not executing!

    I put an alert on delete button click, this is working in case of the initially loaded controls but not for the appended HTML

    1. Not to rewrite the code, but the nice part of asp.net mvc vs. webforms is the ability to have as many ‘forms’ as needed.

      So, let’s say in your list, you are looping through the display.

      Each part can be wrapped in a form .

      foreach…Item item in Model.Items
      Html.BeginForm….(c => c.Delete(item.Id), FormMethod.Post…

  18. I understand what you are trying to suggest Steve!

    MVC does give a lot of flexibility with respect to wrapping but here I am in a situation where the issue is with the JQuery not executing for the newly appended row. I have modified the ID’s of the trigger and the parent using FireBug but neither works, but on page refresh it works just fine!

  19. Hi Steve,

    I was able to solve the issue. I used .bind and .unbind the button click…

    Now I am facing another issue, I want to know if there is a possibility of destroying/removing some html on the page and reloading it.

    For example on a save button click the state of the PopUp Html is reloaded from its initial state not from the state last closed on.

    Thanks again!

    KK

  20. Hi Steve,

    I need to call a JavaScript function just once upon opening a MVC View page.
    I tried to do it from pageLoad(), but pageLoad() it didn’t fire. What is the proper
    way to do this?

    Thanks,

    Paul

  21. Hi Steve,

    I use MS Ajax since the jQuery is not well supported for mobile devices
    my application is intended also.

    Regards,

    Paul

  22. Hi Steve,

    I tried to call pageLoad() from JavaScript in my MVC View, but without
    a success, despite I put the scrip within a ContentTemplate after all
    controlls are rendered as described in the ASP.NET site.

    Regards,

    Paul

  23. Hey, I found your website @ yahoo and i have read some of your other posts. I like what u wright! I just added you to my Google News Reader ;). Keep up the good job. Look forward to see more from you in the future.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s