News

Blog moved to http://blachniet.com

My Stats

  • Posts - 6
  • Comments - 16
  • Trackbacks - 0

Twitter











Recent Comments


Recent Posts


Archives


Post Categories



After writing this post, I realized that it is obnoxiously long, and maybe includes a little bit too much hand-holding. Nevertheless, I decided to post this very detailed step-by-step walkthrough of implementing a simple AJAX form in MVC3 with Razor. I'm probably going also post a shortened version of this topic for those that don't need a complete walkthrogh.

So I've been attempting to dive into ASP.NET MVC 3 lately, and have hit plenty of bumps along the way. This is my first encounter with any ASP.NET, or web development in general, so it is not surprising that I've had so much trouble. I spent about 3 evenings on the problem I'm describing below, so I decided to write up a quick post about it. Please remember that this is my first encounter with ASP.NET, so it is definitely possible that there are better ways to do this. If I do eventually find one, I will update this post. Feel free to educate me if you find any errors.

Problem: I want to have a page that lists a bunch of items (household products in my example). From this page I also want to be able to add items. When I add an item, I don't want to have to reload the entire page, but just the list of items.

Soultion:
For my example I will be using the following model (Product) and data context (BoringStoreContext):

namespace BoringStore.Models
{
    public class Product
    {
        public int ID { getset; }
        public string Name { getset; }
 
        [DataType(DataType.Currency)]
        public decimal Price { getset; }
    }
    public class BoringStoreContext : DbContext
    {
        public DbSet<Product> Products { getset; }
    }
}

Normally I generate a controller with read, write and views already implemented, then just bend it to my will. However, for this post I will do an empty controller so that I can focus on the things we NEED.

// Controllers\ProductController.cs
namespace BoringStore.Controllers
{
    public class ProductController : Controller
    {
        public ActionResult Index()
        {
            BoringStoreContext db = new BoringStoreContext();
            return View(db.Products);
        }
    }
}

This only created the controller for me (in <project>\Controllers), so I now need to create a strongly typed view (Model class = IEnumerable<Product>) in Views\Product. I'm going to create this new view to map to the Index command, so the view will be named Index.cshtml, and I end up with:

@* Views\Product\Index.cshtml *@
@model IEnumerable<BoringStore.Models.Product>
 
@{
    ViewBag.Title = "Index";
}
 
<h2>Index</h2>

So this is the page that I'm going to use to both list Products and allow the user to add Products.We'll start with the list of products. This will be done in its own Partial View, ProductListControl.cshtml.

@* Views\Product\ProductListControl.cshtml *@
@model IEnumerable<BoringStore.Models.Product>
<table>
    <!-- Render the table headers. -->
    <tr>
        <th>Name</th>
        <th>Price</th>
    </tr>
    <!-- Render the name and price of each product. -->
    @foreach (var item in Model)
    { 
        <tr>
            <td>Html.DisplayFor(model => item.Name)</td>
            <td>Html.DisplayFor(model => item.Price)</td>
        </tr>
    }
</table>

Now that we have created our partial view, we need to go back to the Index.cshtml to tell it to render the partial view with the model data passed to it. This is done through the @Html.RenderPartial command. the div id is important as you will see later.

Take a moment to launch your site, visit the ~/Product page, and make sure you see an empty table. If you don't, you did something wrong, so start over.

Now we want to be able to add Products on the same page, but we want to be able to do so without reloading the entire page. We only want to reload the partial view where the new Product will be displayed. To do this we will start in the controller. We now need to be able to pass in a new Product as well as a list of available products to the Index view. This means we need an ProductIndexViewModel. See Rachel Appel's post for more information on ViewModels in MVC here: http://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp.net-mvc-applications

// ViewModels\ProductIndexViewModel.cs
namespace BoringStore.ViewModels
{
    public class ProductIndexViewModel
    {
        public Product NewProduct { getset; }
        public IEnumerable<Product> Products { getset; }
    }
}

We now need to update the controller to build this ViewModel and pass it to the view.

        // In Controllers\ProductController.cs
        public ActionResult Index()
        {
            BoringStoreContext db = new BoringStoreContext();
            ProductIndexViewModel viewModel = new ProductIndexViewModel
            {
                NewProduct = new Product(),
                Products = db.Products
            };
            return View(viewModel);
        }

And now, update the view to use a ProductIndexViewModel. Note that we have to update the model passed to the @Html.RenderPartial command to Model.Products.

@* Views\Product\Index.cshtml *@
@model BoringStore.ViewModels.ProductIndexViewModel
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<div id='productList'>
    @{ Html.RenderPartial("ProductListControl", Model.Products); }
</div>

 

Now that we have the ViewModel, we can start on the form for adding a Product. Let's jump back over to the Controller again, and add the following function:

// In Controllers\ProductController.cs
        public ActionResult Index_AddItem(ProductIndexViewModel viewModel)
        {
            BoringStoreContext db = new BoringStoreContext();
            db.Products.Add(viewModel.NewProduct);
            db.SaveChanges();
 
            return PartialView("ProductListControl", db.Products);
        }

 

This is the action that the AJAX form is going to call to add an item. Here we simply add the item to our database context and then return a PartialView of the ProductListControl view that we created earlier. Now lets insert the AJAX form into the Index view:

<!-- Added to Views\Product\Index.cshtml -->
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
@using (Ajax.BeginForm("Index_AddItem"new AjaxOptions { UpdateTargetId = "productList" }))
{ 
    <div>
        @Html.LabelFor(model => model.NewProduct.Name)
        @Html.EditorFor(model => model.NewProduct.Name)
    </div>
    <div>
        @Html.LabelFor(model => model.NewProduct.Price)
        @Html.EditorFor(model => model.NewProduct.Price)
    </div>
    <div>
        <input type="submit" value="Add Product" />
    </div>
}

Here we have created an AJAX form with lables and editors for the Product. Take note of the UpdateTargetID in the AjaxOptions. This is telling the renderer that the div that we created earlier with the partial view in it is what should be updated with the return value from the action called when this form is submitted. Launch your site and try it out. You should be able to enter data for a new Product, hit the Add Product button, and the table below should update with the product you just added.

Let me know if you have any issues or if you find an alternative that you believe is better.

[TODO Add final source code here]

<!-- Appended to Views\Product\Index.cshtml -->
<div id='productList'>
    @{ Html.RenderPartial("ProductListControl", Model); }
</div>

Comments

Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by byrdman on 8/16/2011 1:58 AM
I was wondering how you handled the javascript being loaded into the partialview. I have an issue where my _layout.cshtml loads all the scripts but the partail view can not make use of them. I end up loading all the scripts in each partial view. But I run into issues when I have more than one Partialview on a single page.
Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by blachniet on 8/16/2011 11:01 AM
I'm not sure why your partial views would not be able to use the javascript that was loaded by your _Layout.cshtml file. The _Layout.cshtml and whatever other cshtml files you use for a view are compiled together to generate a single html file at runtime. Just take a look at the source of the page when you run it, and you will see the <script> tags from the _Layout.cshtml at the top, and the markup for your partial down lower. Can you give a more detailed explanation of the issues you are having?

Thanks!

P.S. This blog has recently moved over to http://www.blachniet.com.
Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by Neero Holan on 11/16/2011 10:32 PM
Hi...
This example was so usefull. Its really really cool
Thanx a lot
Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by sergio on 3/7/2012 3:22 AM
Thanks man!! Very usefull!
Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by Arun on 3/28/2012 9:25 AM
Hi...
This example was so usefull. Its really really cool
Thanx a lot

Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by Sheir on 4/9/2012 12:56 AM
I am new to the MVC3 + Razor and am wondering if a main view can use more than one partial views where some partials are a Form (ie Html.BeginForm() or Ajax.BeginForm()) and each has its own model
(ie one partial has Settings as its model class while another partial has JobStatus as its model class).
Is that possible?

Also what is the difference between the Html and Ajax BeginForms?

Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by Bengaru on 4/28/2012 3:46 AM
Can you plz upload your final source-code
Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by Alberto on 5/8/2012 5:07 AM
Very useful informations, thx a lot
Gravatar # re: [Walkthrough] Updating Partial Views with Unobtrusive AJAX in MVC 3
Posted by SAM on 5/11/2012 12:17 AM
This is one of the best walkthrough on the internet regarding Unobtrusive Ajax. In above scanario you are saving each record on clicking "Add Product" but what if we want to save all products at once? Actually this is the business requirement of our application. We like to give chance to user to add and subtract products and then save record at once.
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: