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!

First look at Knockout Paged SimpleGrid

I have recently been looking at the knockoutjs library and what it can be used for. I started off writing a post on knockout grids. It was getting a little long so I have decided to break it up into a number of posts so they will be shorter and hopefully clearer.

In this first part we are going to take a look at a paged SimpleGird similar to this Paged Grid sample on the KO site.

The current knockout library is at version 3.0 and can be found here or via an easy to install nuget package.  The paged grid sample uses an additional library called knockout.simpleGrid.3.0.js available here.  It is not necessary, but I have local copies of the files and configured a bundle

bundles.Add(new ScriptBundle("~/bundles/knockoutjs").Include(
    "~/scripts/knockout-{version}.js",
    "~/scripts/knockout.simpleGrid.3.0.js"));

Paged Grid
The amount of code needed on the page to create a grid with paging abilities is not much as the code below demonstrates. Obviously the data would not usually be hard coded in the page, data would usually be retrieved from a database.

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<div data-bind='simpleGrid: gridViewModel'></div>

@section Scripts {
    @Scripts.Render("~/bundles/knockoutjs")

    <script type="text/javascript">

        var PagedGridModel = function (items) {

            this.items = ko.observableArray(items);

            this.gridViewModel = new ko.simpleGrid.viewModel({
                data: this.items,
                columns: [
                    { headerText: "Person ID", rowText: "employeeid" },
                    { headerText: "Title", rowText: "title" },
                    { headerText: "Forename", rowText: "forename" },
                    { headerText: "Surname", rowText: "surname" },
                    { headerText: "Start Date", rowText: "startdate" }
                ],
                pageSize: 3
            });
        }

        var gridData = [
            { employeeid: 1000, title: "Mr", forename: "Fred", surname: "Flinstone", startdate: "12 May 1953" },
            { employeeid: 1001, title: "Mr", forename: "Barney", surname: "Rubble", startdate: "20 Jun 1978" },
            { employeeid: 1002, title: "Ms", forename: "Belinda", surname: "Mason", startdate: "11 Nov 2004" },
            { employeeid: 1003, title: "Mrs", forename: "Mary", surname: "Moore", startdate: "5 Aug 1999" },
            { employeeid: 1004, title: "Mr", forename: "Harry", surname: "Hill", startdate: "1 Mar 1995" },
            { employeeid: 1005, title: "Dr", forename: "Carl", surname: "Flowers", startdate: "11 Sep 1988" },
            { employeeid: 1006, title: "Miss", forename: "Jenna", surname: "Pray", startdate: "5 Dec 2010" }
        ];
        ko.applyBindings(new PagedGridModel(gridData));
    </script>
}

The page size is set to 3, although it could be set to anything as appropriate. With a size of 3 I could keep the demo data to a minimum.

Because the grid is dynamically created you will not see the html markup of the table when viewing the page source, right-click and View source in IE or View page source in Chrome. Though if you inspect the page via the Developer Tool of you browser you can see how knockout has constructed the table.

<div data-bind="simpleGrid: gridViewModel">
    <table class="ko-grid" cellspacing="0">
        <thead>
            <tr data-bind="foreach: columns">
                <th data-bind="    text: headerText">Person ID</th>
                <th data-bind="    text: headerText">Title</th>
                <th data-bind="    text: headerText">Forename</th>
                <th data-bind="    text: headerText">Surname</th>
                <th data-bind="    text: headerText">Start Date</th>
            </tr>
        </thead>
        <tbody data-bind="    foreach: itemsOnCurrentPage">
            <tr data-bind="    foreach: $parent.columns">
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">1000</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Mr</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Fred</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Flinstone</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">12 May 1953</td>
            </tr>
            <tr data-bind="    foreach: $parent.columns">
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">1001</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Mr</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Barney</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Rubble</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">20 Jun 1978</td>
            </tr>
            <tr data-bind="    foreach: $parent.columns">
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">1002</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Ms</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Belinda</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Mason</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">11 Nov 2004</td>
            </tr>
            <tr data-bind="    foreach: $parent.columns">
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">1003</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Mrs</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Mary</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">Moore</td>
                <td data-bind="    text: typeof rowText == 'function' ? rowText($parent) : $parent[rowText]">5 Aug 1999</td>
            </tr>
        </tbody>
    </table>
    <div class="ko-grid-pageLinks"><span>Page:</span>
        <!-- ko foreach: ko.utils.range(0, maxPageIndex) -->
        <a class="selected" href="#" data-bind="    text: $data + 1, click: function () { $root.currentPageIndex($data) }, css: { selected: $data == $root.currentPageIndex() }">1</a>                                                       <a href="#" data-bind="    text: $data + 1, click: function () { $root.currentPageIndex($data) }, css: { selected: $data == $root.currentPageIndex() }">2</a>                                                       <a href="#" data-bind="    text: $data + 1, click: function () { $root.currentPageIndex($data) }, css: { selected: $data == $root.currentPageIndex() }">3</a>
        <!-- /ko -->
    </div>
</div>

The bottom div with a class of ko-grid-pageLinks contains the pager.
We will see code like this again when looking at custom grid templates later.