ASP.NET MVC Framework has built-in Ajax enabled helper methods for unobtrusive Ajax support, which simplify the process for Ajax calls in your MVC Web Applications. Those Ajax methods are based of course in jQuery, so if you are familiar with the latter, learning to use them will be a piece of cake. We have shown the tranditional way to make Ajax calls in MVC applications in previous post. This post will guide you step by step showing you an alternative and more straight forward way to make Ajax calls through the built-in helper methods which eliminates the need to add blocks of code in your Views. Let’s start.
Start by creating a new ASP.NET MVC Web Application named “MVCUnobtrusiveAjax”, choosing the Basic template and Razor for view engine. We will create first our domain model. Create a C# class file named “Product.cs” in your models folder. Add a Product class and a Category enumeration as follow.
namespace MVCUnobtrusiveAjax.Models { public class Product { public int ProductId; public string Name; public string Description; public decimal Price; public Category Category; } public enum Category { Car, Airplane, Bicycle, Motorcycle } }
Add an Empty MVC “ProductController” controller with three action methods in the controllers folder with the following contents.
using MVCUnobtrusiveAjax.Models; namespace MVCUnobtrusiveAjax.Controllers { public class ProductController : Controller { private Product[] products = { new Product { ProductId = 1, Name = "Audi A3", Description = "New Audi A3", Category = Category.Car, Price = 25000 }, new Product { ProductId = 2, Name = "VW Golf", Description = "New VW Golf", Category = Category.Car, Price = 22000 }, new Product { ProductId = 3, Name = "Boing 747", Description = "The new Boing airplane", Category = Category.Airplane, Price = 2000000 }, new Product { ProductId = 4, Name = "Boing 747", Description = "The new Boing airplane", Category = Category.Airplane, Price = 2000000 }, new Product { ProductId = 5, Name = "Yamaha 250", Description = "Yamaha's new motorcycle", Category = Category.Motorcycle, Price = 5000 }, new Product { ProductId = 6, Name = "honda 750", Description = "Honda's new motorcycle", Category = Category.Motorcycle, Price = 7000 } }; public ActionResult Index() { return View(); } public ActionResult GetProducts() { return View(products); } [HttpPost] public ActionResult GetProducts(string selectedCategory) { if (selectedCategory == null || selectedCategory == "All") { return View(products); } else { Category selected = (Category)Enum.Parse(typeof(Category), selectedCategory); return View(products.Where(p => p.Category == selected)); } } } }
This class uses some mock Product objects and has two basic operations, the GetProducts() and the GetProducts(String selectedCategory). The first one, will display all available products while the second one, the selected category’s only. Right click inside a GetProducts action and add a View with the same name. Fill IEnumerable<Product> in the Model class (Strongly-typed View).
@using MVCUnobtrusiveAjax.Models @model IEnumerable<Product> @{ ViewBag.Title = "Get Products"; } <h2>Get Products</h2> <table style="background-color:lightcoral"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Category</th> <th>Price</th> </tr> </thead> <tbody> @foreach (Product p in Model) { <tr> <td>@p.ProductId</td> <td>@p.Name</td> <td>@p.Description</td> <td>@p.Category</td> <td>@p.Price</td> </tr> } </tbody> </table> @using (Html.BeginForm()) { <div> @Html.DropDownList("selectedCategory", new SelectList( new[] { "All" }.Concat(Enum.GetNames(typeof(Category))))) <button type="submit">Submit</button> </div> }
The Html.BeginForm() helper method, ensures that the form will post back to the [HttpPost] GetProducts action method the selectedCategory value. Before running your application you may want to change the default routing values for the controller and action segments in the RouteConfig.cs file like this.
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Product", action = "GetProducts", id = UrlParameter.Optional } ); }
This way when your start your application you will redirected immediately to the following View.
At this time, your application works fine, you can select a category, click the Submit button and get the respective results. Though, Unobtrusive Ajax isn’t supported yet, hence you get a full page reloading. To enable it, make sure you have a key=”UnobtrusiveJavaScriptEnabled” value=”true” in your root Web.config file under the appSettings element.
<appSettings> <add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>
Then add jQuery support in the central _Layout.cshtml file. Remove the script sections and style sections and add two references from your script folder. You may have different versions on your scripts folder, so make sure you use the right ones.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <script src="~/Scripts/jquery-1.8.2.min.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script> </head> <body> @RenderBody() </body> </html>
Next thing we have to do, is to refactor our ProductController. Replace the old GetProducts actions with the following two.
public PartialViewResult GetProductData(string selectedCategory = "All") { IEnumerable<Product> data = products; if (selectedCategory != "All") { Category selected = (Category)Enum.Parse(typeof(Category), selectedCategory); data = products.Where(p => p.Category == selected); } return PartialView(data); } public ActionResult GetProducts(string selectedCategory = "All") { return View((object)selectedCategory); }
We are going to use the GetProductData to render a partial View which will update our table rows according to the selected category. On the other hand, the GetProducts action method now will just pass to the View the selectedCategory value. This way we simplified our code removing the [HttpPost] attribute. Now we have to create our Partial View which will update the table contents. Right click in the GetProductData action and add a Partial View with strong-type IEnumerable<Product>. Paste the following contents.
@using MVCUnobtrusiveAjax.Models @model IEnumerable<Product> @foreach (Product p in Model) { <tr> <td>@p.ProductId</td> <td>@p.Name</td> <td>@p.Description</td> <td>@p.Category</td> <td>@p.Price</td> </tr> }
Change your GetProducts View contents as follow. We need to pass a string as the Model this time, since we have changed our respective action method to pass the selected category.
@using MVCUnobtrusiveAjax.Models @model string @{ ViewBag.Title = "Get Products"; } <h2>Get Products</h2> <table style="background-color: lightcoral"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Category</th> <th>Price</th> </tr> </thead> <tbody> @Html.Action("GetProductData", new { selectedCategory = Model }) </tbody> </table> @using (Html.BeginForm()) { <div> @Html.DropDownList("selectedCategory", new SelectList( new[] { "All" }.Concat(Enum.GetNames(typeof(Category))))) <button type="submit">Submit</button> </div> }
Still your application works fine but not Ajax asynchronous calls. Though we render our table contents through an action method which return a partial View. It’s time to enable the Unobtrusive Ajax in our View. Change the GetProducts contents again, like this.
@using MVCUnobtrusiveAjax.Models @model string @{ ViewBag.Title = "Get Products"; AjaxOptions ajaxOptions = new AjaxOptions { UpdateTargetId = "productsTable" }; } <h2>Get Products</h2> <table style="background-color: lightcoral"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Category</th> <th>Price</th> </tr> </thead> <tbody id="productsTable"> @Html.Action("GetProductData", new { selectedCategory = Model }) </tbody> </table> @using (Ajax.BeginForm("GetProductData", ajaxOptions)) { <div> @Html.DropDownList("selectedCategory", new SelectList( new[] { "All" }.Concat(Enum.GetNames(typeof(Category))))) <button type="submit">Submit</button> </div> }
Build and run your applications. Select a category and ensure that this time you get your results asynchronously. What we did above is to use an Ajax.BeginFrom Ajax helper method which takes the target action method to invoke and an AjaxOptions object parameter. We configured this object in 5-8 requesting the returned data to replace the “UpdateTargetId” tbody element. So simple. If you want to provide the user with a feedback while making the ajax request you can do it this way.
@using MVCUnobtrusiveAjax.Models @model string @{ ViewBag.Title = "Get Products"; AjaxOptions ajaxOptions = new AjaxOptions { UpdateTargetId = "productsTable", LoadingElementId = "loadingProducts", LoadingElementDuration = 1000 }; } <h2>Get Products</h2> <div id="loadingProducts" style="background-color:cadetblue; display:none"> <p>Loading Products...</p> </div> @* Other code ommited *@
If you wish to prompt the user before making an Ajax request you can to as follow.
AjaxOptions ajaxOptions = new AjaxOptions { UpdateTargetId = "productsTable", LoadingElementId = "loadingProducts", LoadingElementDuration = 1000, Confirm ="Do you really want to display products?" };
Clicking OK will proceed the Ajax request while selecting Cancel will cancel it. We have used an Ajax.BeginForm helper method to pass the category value in the GetProductData action. What if we wanted to make the same thing through links? You can do this using the Ajax.ActionLink helper method like this.
@*Code ommited *@ <table style="background-color: lightcoral"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Category</th> <th>Price</th> </tr> </thead> <tbody id="productsTable"> @Html.Action("GetProductData", new { selectedCategory = Model }) </tbody> </table> <hr /> <div> @foreach (string category in Enum.GetNames(typeof(Category))) { <div class="ajaxLink"> @Ajax.ActionLink(category, "GetProductData", new { selectedCategory = category }, new AjaxOptions { UpdateTargetId = "productsTable" }) </div> } </div> @* Code ommited *@
Click a category link and notice that you get the same Ajax result. The last thing I wanna show you is the CallBack functions OnBegin, OnSuccess, OnFailure, OnComplete. Add a script with functions in the GetProducts View and bind them to the Ajax links through the AjaxOptions as follow.
@using MVCUnobtrusiveAjax.Models @model string @{ ViewBag.Title = "Get Products"; AjaxOptions ajaxOptions = new AjaxOptions { UpdateTargetId = "productsTable", LoadingElementId = "loadingProducts", LoadingElementDuration = 1000, Confirm = "Do you really want to display products?" }; } <script type="text/javascript"> function OnAjaxRequestBegin() { alert("This is the OnBegin Callback"); } function OnAjaxRequestSuccess(data) { alert("This is the OnSuccessCallback: " + data); } function OnAjaxRequestFailure(request, error) { alert("This is the OnFailure Callback:" + error); } function OnAjaxRequestComplete(request, status) { alert("This is the OnComplete Callback: " + status); } </script> <h2>Get Products</h2> <div id="loadingProducts" style="background-color: cadetblue; display: none"> <p>Loading Products...</p> </div> <table style="background-color: lightcoral"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Category</th> <th>Price</th> </tr> </thead> <tbody id="productsTable"> @Html.Action("GetProductData", new { selectedCategory = Model }) </tbody> </table> <hr /> <div> @foreach (string category in Enum.GetNames(typeof(Category))) { <div class="ajaxLink"> @Ajax.ActionLink(category, "GetProductData", new { selectedCategory = category }, new AjaxOptions { UpdateTargetId = "productsTable", OnBegin = "OnAjaxRequestBegin", OnFailure = "OnAjaxRequestFailure", OnSuccess = "OnAjaxRequestSuccess", OnComplete = "OnAjaxRequestComplete"}) </div> } </div> @using (Ajax.BeginForm("GetProductData", ajaxOptions)) { <div> @Html.DropDownList("selectedCategory", new SelectList( new[] { "All" }.Concat(Enum.GetNames(typeof(Category))))) <button type="submit">Submit</button> </div> }
Click a Category link and pay attention to the Ajax request life-cycle.
That’s it, this is the way to use Unobtrusive Ajax in ASP.NET MVC applications. In later post, we ‘ll see how to get JSON data from your actions methods when using Unobtrusive Ajax. You can download the MVCUnobtrusiveAjax project we have created from here.
In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.
.NET Web Application Development by Chris S. | |||
![]() |
![]() |
Categories: ASP.NET
I would like to thank you for the efforts you have
put in writing this site. I really hope to check out the same high-grade blog posts by you later on as well.
In truth, your creative writing abilities has inspired
me to get my own blog now 😉
thanks for your good words friend, good luck with your blog and welcome to the bloggers community..
I was so frustrated prior to working through your article. I even started reading your article and said “No – this doesn’t work!”. But then I stopped. I thought – This guy wouldn’t have made an article like this without having it working… So, I went back to basics. I followed each of your steps, duplicating them on my end as I went. Success.
Thanks
Hey Buddy,
Thanks for the post..
Easy to understand.. 🙂
Thanks for the posting this article, but I have some issues while duplicating the steps you mentioned above.
I am new to ASP.NET MVC and I was having problem with AJAX. I came across your article and followed each and every step, but still when I click submit, the partial view is rendered on a new page rather than on the same page.
Hi Soyeb, download and run the project we built on this post (check link at the end of the article) and check if you missed something. Regards, C.S
Hi there! I could have sworn I’ve been to this website before but
after browsing through some of the post I realized it’s
new to me. Anyways, I’m definitely happy I found it and I’ll be
bookmarking and checking back frequently!
Reblogged this on sharepointswe.
Can I still download your code? The page says it can’t find the link
thanks
This is awesome. It hits all of the salient points of MVC5 Ajax, without straying away.