How to update partial view using jquery ajax Part 2

This follows on from a previous post, Make sure you have read How to update partial view using jquery ajax before continuing or some of this may not make sense.

In the previous post the partial view was updated when a selection was made from a drop down list. When the main view first loaded there was no code to populate the partial view. The purpose here is to change the original code to allow the partial view to be loaded when the main view is initially loaded.

ViewModels
Create a new view model for the main index view. The CatalogueViewModel contains a DateTime and a list of products. This is the list which will be used to initially populate the partial view.

public class CatalogueViewModel
{
    public DateTime CatalogueDate { get; set; }
    // any other required properties
    public List<Product> Products { get; set; }
}

Main Index View
The main view has changed slightly, As a CatalogueViewModel model is now passed to the view the strongly-typed model declaration needs to change.

@model MVCUpdatePartial.Models.CatalogueViewModel

Also the date displayed is now taken from the view model.

Catalogue Date:
@Model.CatalogueDate.ToLongDateString()    
@Model.CatalogueDate.ToLongTimeString()

The select list for selecting the quantity of products to display now has an additional All option. This is to accommodate the fact that when the view loads initially the quantity is not specified and all products will initially be loaded.

<select id="PageSize">
    <option value="">All</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10</option>
</select>

The div element where the partial view is loaded now has a command to explicitly render the partial view. This will render the view and display a list of products when the main view is initially loaded.

<div id="SelectedProducts">
    @{Html.RenderPartial("_ProductList", Model.Products);}
</div>

Controller
The Index (GET) Action method now passes a CatalogueViewModel to the Index view. Initially the products list is populated with all products (OK for this scenario as there are not that many).
Also the GetProducts (POST) Action method handles the situation where a null is passed in the quantity parameter. This will happen when the All option is selected from the select list.

public ActionResult Index()
{
    var catalogueVM = new CatalogueViewModel
    {
        CatalogueDate = DateTime.Now,
        Products = products
    };
    return View(catalogueVM);
}

[HttpPost]
public ActionResult GetProducts(int? quantity)
{
    var selectedProducts = products.Take(quantity ?? products.Count);
    return PartialView("_ProductList", selectedProducts);
}

Done!

How to update partial view using jquery ajax

I wanted to give a quick but clear example how to use jQuery to update a partial view, an example which could be easy to replicate.

We need a model
The model consists of product class with three properties; a product code, name and price.

public class Product
{
    [Required]
    public string Code { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    [DataType(DataType.Currency)]
    public decimal UnitPrice { get; set; }
}

And a Controller
For simplicity there is no specific data layer or repository, the controller contains a global array of products. When a http post is made to the GetProducts ActionResult a parameter is passed specifying how many products to return.

public class HomeController : Controller
{
    private List<Product> products = new List<Product>() ;

    public HomeController()
    {
        products.Add(new Product { Code = "P001", Name = "Hair Dryer", UnitPrice = 10.50M });
        products.Add(new Product { Code = "P002", Name = "Carpet Cleaner", UnitPrice = 3.60M });
        products.Add(new Product { Code = "P003", Name = "Hair Remover", UnitPrice = 12.10M });
        products.Add(new Product { Code = "P004", Name = "Vacuum Cleaner", UnitPrice = 80.99M });
        products.Add(new Product { Code = "P005", Name = "Table", UnitPrice = 7.69M });
        products.Add(new Product { Code = "P006", Name = "Duvet Cover", UnitPrice = 12.89M });
        products.Add(new Product { Code = "P007", Name = "Towel", UnitPrice = 3.50M });
        products.Add(new Product { Code = "P008", Name = "Electric Oven", UnitPrice = 125.99M });
        products.Add(new Product { Code = "P009", Name = "Hair Straighteners", UnitPrice = 8.90M });
        products.Add(new Product { Code = "P010", Name = "Belt", UnitPrice = 3.50M });
    }

    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult GetProducts(int quantity)
    {
        var selectedProducts = products.Take(quantity);
        return PartialView("_ProductList", selectedProducts);
    }
}

The Main View
mvcpartialviewupdate-main-view
Selecting from the dropdown list causes a change event the initiates the post to the server. If the post is successful the partial view is updated.
The date and time is displayed for comparison with the date and time on the partial view, they will be different.

<h2>Product Display</h2>
<div class="col-sm-6">
    @DateTime.Now.ToLongDateString()
    @DateTime.Now.ToLongTimeString()
<div class="panel panel-default">
<div class="panel-heading">
            Products</div>
<div class="panel-body">
<div class="form-group form-group-sm">
                <label class="control-label" for="PageSize">
                    How many products should be displayed?
                </label>
<div>
                    <select id="PageSize">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select></div>
</div>
<div id="SelectedProducts"></div>
</div>
</div>
</div>

jQuery AJAX
When a selection is made from the dropdown list the change event is triggered and a POST made to the server.

@section scripts{
    <script type="text/javascript">

        $(document).ready(function () {

            $("#PageSize").on("change", function () {
                var val = $('#PageSize').val();
                $.ajax({
                    url: "/Home/GetProducts",
                    type: "POST",
                    data: { quantity: val }
                })
                .done(function (partialView) {
                    $("#SelectedProducts").html(partialView);
                });
            });
        });
    </script>
}

And the Partial View (_ProductList.cshtml)
The partial view displays a list of products in a table. The POST date and time is displayed for comparison purposes.

@model IEnumerable<MVCUpdatePartial.Models.Product>
<div class="editor-label">
    Selected Products</div>
<table class="table">
    @foreach(var product in Model)
    {
<tr>
<td>@product.Code</td>
<td>@product.Name</td>
<td>@product.UnitPrice</td>
</tr>
}</table>
Updated: @DateTime.Now.ToLongDateString()
@DateTime.Now.ToLongTimeString()

After the POST
And how the view looks after the POST and the partial view is updated.
mvcpartialviewupdate-main-view-after-post

Quick Introduction to Page WebMethods

PageMethods can be implemented by the following steps.

1. Include the Services namespace.

using System.Web.Services;

2. Add a public static method to your code behind page and decorate with [WebMethod].

[WebMethod]
public static string GetSalutation()
{
    var salutation = string.Empty;
    var hour = DateTime.Now.TimeOfDay.Hours;
    if (hour >= 0 && hour <= 11)
    {
         salutation = "Good Morning!";
    }
    else if (hour >= 12 && hour <= 16)
    {
        salutation = "Good Afternoon!";
    }
    else
    {
        salutation = "Good Evening!";
    }
     return salutation;
}

3. Add ScriptManager control from the Ajax Extensions tab on the Visual Studio toolbox.

4. Enable page methods in your app by adding EnablePageMethods=”true” to the Scriptmanager markup.

5. Write javascript to call the pagemethod and handle the result.

function GetGreeting()
{
    PageMethods.GetSalutation(onSucess, onError);

    function onSucess(result)
    {
        var ctrl = document.getElementById("Greeting");
        ctrl.value = result;
    }

    function onError(result)
    {
        alert('Cannot process your request at the moment, please try later.');
    }
}

6. Create an interface with elements for invoking the javascript method and displaying the results;

<input onclick="GetGreeting()" type="button" value="Go" />
<input id="Greeting" type="text" value="" />

Possible Issues

1. The scriptmanager must be placed inside the pages Form tags, it it is not an exception will be thrown;

‘ScriptManager’ must be placed inside a form tag with runat=server

2. If you miss off enabling the pagemethods on the scriptmanager a javascript error will be thrown;

‘PageMethods’ is undefined

Final Note

Interacting with page controls must be done in the javascript side as page controls cannot be accessed from with a pagemethod. So any values you need from page controls will need to be passed to the page methods as parameters.

I have added a visual studio project containing a couple of examples including the one above to Github.