DotNetCurry Logo

Master-Details View in ASP.NET MVC using WEB API, jQuery and Knockout.js

Posted by: Mahesh Sabnis , on 10/16/2014, in Category ASP.NET MVC
Views: 40255
Abstract: A master-detail type of view in an ASP.NET application is a common requirement that developers face. This article shows how to use a mash up of server and client-side technologies like ASP.NET MVC, WEB API, jQuery Ajax and Knockout to implement Master-Details type of UI in an LOB web application

One of the top-most requirements that a developer comes across while developing Line of Business (LoB) applications is to provide users with a way to view a Master list of product they are managing. When they click on any of the rows, a Details view appears with all records that are related to the Master view. This is also referred to as a Master-Detail Tabular view. For example, in an order management application, a detail view could provide information about Orders placed by Customers (Master view).

 

In the article, we will be implementing a master-details view in ASP.NET MVC, Web API and Knockout.js, which will provide Customer-wise Order Details.

This article assumes you know Knockout.js and ASP.NET Web API. If you are new to it, read Getting Started with KnockoutJS in ASP.NET MVC and Head First into ASP.NET Web API – A Time Card Service using Media Formatters and KnockoutJs. You will find plenty of other articles on these technologies if you just search this site.

Implementing Master Details Application in ASP.NET MVC, Web API and Knockout.js

Step 1: Open Visual Studio 2013 (I am using Ultimate with Update 3 but that’s not really a requirement). Create an Empty ASP.NET MVC application with the name ‘A4_Master_Details’.

Step 2: In the App_Data folder, add a new SQL Server Database with the name ‘Applications.mdf’. In this database, add the following tables:

Customer

CREATE TABLE [dbo].[Customer] (
    [CustomerId]   INT           IDENTITY (1, 1) NOT NULL,
    [CustomerName] VARCHAR (200) NOT NULL,
    [Email]        VARCHAR (100) NOT NULL,
    [ContactNo]    VARCHAR (100) NOT NULL,
    PRIMARY KEY CLUSTERED ([CustomerId] ASC)
);

Orders

CREATE TABLE [dbo].[Orders] (
    [OrderId]         INT           IDENTITY (1, 1) NOT NULL,
    [CustomerId]      INT           NOT NULL,
    [OrderedItem]     VARCHAR (100) NOT NULL,
    [OrderedQuantity] INT           NOT NULL,
    [OrderedDate]     DATETIME      NOT NULL,
    PRIMARY KEY CLUSTERED ([OrderId] ASC),
    FOREIGN KEY ([CustomerId]) REFERENCES [dbo].[Customer] ([CustomerId])
);

Step 3: To create a model, in the Models Folder add a ADO.NET Entity Framework and using the Database First approach, complete the wizard by selecting Applications.mdf database and its Customer and Orders Table. After completing the wizard, the mapping will be displayed as shown here:

customer-order

Step 4: To add the ASP.NET WEB API controller, right-click on the Controllers folder and add an Empty WEB API controller. Name the API controller as ‘CustomerOrderAPIController’. In this controller, add the following action methods:

using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using A4_Master_Details.Models;

namespace A4_Master_Details.Controllers
{
    public class CustomerOrderAPIController : ApiController
    {
        ApplicationsEntities ctx;
        public CustomerOrderAPIController()
        {
            ctx = new ApplicationsEntities(); 
        }

        [Route("Customers")]
        public List<Customer> GetCustomers()
        {
            var Res = (from c in ctx.Customers.ToList()
                      select new Customer()
                      {
                           CustomerId = c.CustomerId,
                           CustomerName = c.CustomerName,
                           Email = c.Email,
                           ContactNo = c.ContactNo
                      }).ToList();
            return Res;
        }

        [Route("Customer/Orders/{id}")]
        public List<Order> GetOrders(int id)
        {
            var Res = (from o in ctx.Orders.ToList()
                       where o.CustomerId == id
                       select new Order()
                       {
                            OrderId = o.OrderId,
                            OrderedItem = o.OrderedItem,
                            OrderedQuantity = o.OrderedQuantity,
                            OrderedDate = o.OrderedDate
                       }).ToList();

            return Res;
        }
    }
}

The GetCustomers() and GetOrders() action methods are used to retrieve the Customers and Orders information based upon the CustomerId.

Step 5: In the Controllers folder, add an Empty MVC controller, name it as ‘CustomerOrderController’. In this controller, the Index action method is available.

Step 6: From the Index method of the MVC controller, scaffold the Empty View of name Index.cshtml.

Step 7: To implement the Client-Side logic, we need to add jQuery, jQuery UI and Knockout.js libraries. In then Scripts folder, add a new folder with the name ‘MyScripts’ and add a new JavaScript file in it called ‘MasterDetailsLogic.js’. Add the following code in this file:

/// <reference path="../jquery-2.1.1.min.js" />
/// <reference path="../jquery-ui.min-1.11.1.js" />
/// <reference path="../knockout-3.2.0.js" />
(function () {

    var currentWin = $(window); //The Windows Object
    var divDialog = $("#detailcontainer");
    var top = (currentWin.height() - divDialog.height()) / 2; //Get the Top
    var left = (currentWin.width() - divDialog.width()) / 2; //and the left

    //The Dialog object
    $("#detailcontainer").dialog({
        autoOpen: false,
        width: 400,
        minHeight: 250,
        maxHeight: 350,
        resizable: true
    });

    //Set the Position for the dialog
    divDialog.css({
        position: "center",
        top: top,
        left: left
    });



    var MSViewModel = function () {

        var self = this;

        self.Customers = ko.observableArray([]); //Array for Customers
        self.Orders = ko.observableArray([]); //Array for Orders
        self.ErrorMessage = ko.observable(); //The Error Message
        self.CustomerId = ko.observable(); //The Customer Id
        self.CustomerName = ko.observable(); //The Customer NAme


        //The Customer Object
        var Customer = {
            CustomerId: self.CustomerId,
            CustomerName:self.CustomerName
        }

        loadCustomers();

        //The Fucntion to make call to WEB API
        //and returns the Customer Object 
        function loadCustomers() {
            $.ajax({
                url: "/Customers",
                type:"GET"
            }).done(function (resp) {
                self.Customers(resp);
            }).error(function (err) {
                self.ErrorMessage("Error!!!!" + err.status);
            });
        };

        //The Function to Open and Show the Dialog box 
        self.openDetailsDialog = function (cust) {
            self.CustomerName(cust.CustomerName); //USed to Display Customer Name
            getOrdersByCustomers(cust)
            $("#detailcontainer").dialog("open");
        }

        //Function call WEB API to Get Orders for the Customer 
        function getOrdersByCustomers(cust) {
            $.ajax({
                url: "/Customer/Orders/" + cust.CustomerId,
                type:"GET"
            }).done(function (resp) {
                self.Orders(resp);
            }).error(function (err) {
                self.ErrorMessage("Error! " + err.status);
            });
        }

    };

    ko.applyBindings(new MSViewModel());

})();

The above JavaScript does the following :

  • We start with getting the top and left coordinate based upon the windows object. This is required to specify the position for the Details view dialog box
  • The Knockout view model of name ‘MSViewModel’ is written which defines an ObservableArray for Customer and Orders
  • The ‘loadCustomers’ functions makes an Ajax call to the WEB API and retrieves the Customers data
  • The ‘getOrdersByCustomers’ function accepts a customer object and based upon the CustomerId, the Orders information is retrieved by making an Ajax call to WEB API
  • The ‘openDetailsDialog()’ function open the dialog box which is applied on the <div> using jQuery UI dialog object

Step 8: In the Index.cshtml I have added some <style> (keep separately for production apps) and  the HTML Markup with DataBinding with an observables declared in the ViewModel:

@{
    ViewBag.Title = "Index";
}
<style type="text/css">
    table, td {
      border:double;
    }
    thead {
      background-color:azure;
      font:bold;
      font-size:15px;
    }
    #detailcontainer {
       background-color:lightgray;
    }
    #dvtablecontainer {
      height:200px;
      overflow-y:scroll;
    }
</style>

<script src="~/Scripts/jquery-2.1.1.min.js"></script>
<script src="~/Scripts/jquery-ui.min-1.11.1.js"></script>
<script src="~/Scripts/knockout-3.2.0.js"></script>

<h1>Master-Details Customer-Orders</h1>

<h2>The Customer Table</h2>
<table>
    <thead>
        <tr>
            <td>
                Id
            </td>
            <td>
                Name
            </td>
            <td>
                Email
            </td>
            <td>
                Contact No
            </td>
        </tr>
    </thead>
    <tbody data-bind="foreach:Customers">
        <tr class="c1" data-bind="click:$root.openDetailsDialog">
            <td> <span data-bind="text:CustomerId"></span></td>
            <td> <span data-bind="text:CustomerName"></span></td>
            <td> <span data-bind="text:Email"></span></td>
            <td> <span data-bind="text:ContactNo"></span></td>
        </tr>
    </tbody>
</table>

<div id="detailcontainer">
    <h4>Orders Placed by : <span data-bind="text:CustomerName"></span></h4>
    <div id="dvtablecontainer">
        <table>
            <thead>
                <tr>
                    <td>
                        OrderId
                    </td>
                    <td>
                        OrderedItem
                    </td>
                    <td>
                        OrderedQuantity
                    </td>
                    <td>
                        OrdereDate
                    </td>
                </tr>
            </thead>
            <tbody data-bind="foreach:Orders">
                <tr>
                    <td> <span data-bind="text:OrderId"></span></td>
                    <td> <span data-bind="text:OrderedItem"></span></td>
                    <td> <span data-bind="text:OrderedQuantity"></span></td>
                    <td> <span data-bind="text:OrderedDate"></span></td>
                </tr>
            </tbody>
        </table>

    </div>
</div>

<script src="~/Scripts/MyScripts/MasterDetailsLogic.js"></script>

In the above markup:

- The first table is used to show the Customers details. The <tbody> for this table is ‘foreach’ bound with the Customers observable array. The <tr> under the <tbody> is click bound with the ‘openDetailsDialog’ function declared in the ViewModel.

- The <div> with id ‘detailcontainer’ is set to the jQuery UI’s ‘dialog’ element in the JavaScript added in Step 7. This <div> contains a <span> which is ‘text’ bound with the CustomerName observable. This <div> contains another <div> in it with id as ‘dvtablecontainer’. This div contains <table> of which <tbody> if ‘foreach’ bind with the Orders.

Run the application.

After running the application, the result will be displayed as shown here:

master-details-mvc-knockout

The above table shows the ‘Customers’ master details. To View all Orders details placed by the customer, click on the Customer row on the table and it will show a Dialog box with orders placed by the selected customer:

master-details-webapi-knockout

Conclusion

A mash up of server and client-side technologies like ASP.NET MVC, WEB API, jQuery Ajax and Knockout to implement Master-Details type of UI requirements easily in an LOB web application.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!