Click here to Skip to main content
15,891,633 members
Articles / Web Development / HTML

MVC Techniques with jQuery, JSON, Knockout, and C#

Rate me:
Please Sign up or sign in to vote.
4.94/5 (138 votes)
2 Jan 2012CPOL14 min read 439.3K   22.4K   415  
Developing an Order Entry application with MVC.
@using System.Collections;
@using System.Collections.Generic;

@model NorthwindViewModel.OrderViewModel
@{
    ViewBag.Title = "Order Entry Detail";
    
    ArrayList orderDetails = new ArrayList();

    foreach (var item in Model.OrderDetailsProducts)
    {                                            
        var orderDetail = new
	    {            
            ProductID = item.OrderDetails.ProductIDFormatted,
	        ProductName = item.Products.ProductName,
            Quantity = item.OrderDetails.Quantity,
            UnitPrice = item.OrderDetails.UnitPriceFormatted,
            QuantityPerUnit = item.Products.QuantityPerUnit,
            Discount = item.OrderDetails.DiscountFormatted            
        };

        orderDetails.Add(orderDetail);
    }

}

<h4>Order Entry Detail</h4>

<div style="float:left; width:150px; height:25px; text-align:right;" class="field-label" >Order Number:&nbsp;</div>
<div style="float:left; width:150px; height:25px; font-weight:bold; " >&nbsp;@Model.Order.OrderID</div>
<div style="float:left; width:150px; height:25px; text-align:right; " class="field-label">Customer ID:&nbsp;</div>
<div style="float:left; width:150px; height:25px; ">&nbsp;@Model.Customer.CustomerID</div>

<div style="clear:both;"></div>  

<div style="float:left; width:150px; text-align:right; height:25px;" class="field-label" >Order Date:&nbsp;</div>
<div style="float:left; width:150px; height:25px; ">&nbsp;@Model.Order.OrderDate.ToShortDateString()</div>                       
<div style="float:left; width:150px; height:25px; text-align:right;" class="field-label">Customer Name:&nbsp;</div>
<div style="float:left; height:25px; ">&nbsp;@Model.Customer.CompanyName</div>

<div style="clear:both;"></div>  

<!--====== Container ======-->
<table border="0" cellpadding="0"  cellspacing="0" style="width:100%">
<tr class="DataGridHeader">
<td style="width:10%; height:25px">Product ID</td>
<td style="width:30%">Product Description</td>
<td style="width:10%">Quantity</td>
<td style="width:10%">Unit Price</td>
<td style="width:15%">UOM</td>
<td style="width:10%">Discount</td>
<td style="width:15%">Edit Options</td>
</tr>
<tbody data-bind='template: {name: "OrderDetailTemplate", foreach: LineItems}'> </tbody> 
</table>


<div data-bind="visible: AddNewLineItem">
    <table style="width: 100%">
        <tr>
            <td style="width: 10%">
                <input id="ProductID" type="text" value="" onkeypress="ProductIDEntered(this,event);" style="width: 50px" />&nbsp;
                <img alt="Product Inquiry" title="Product Inquiry" style="vertical-align:middle" width="20px" height="20px"  onclick="ShowProductInquiryModal()" src="@Url.Content("~/Content/Images/search3.gif")" />
            </td>
            <td style="width: 30%">
                <span id="ProductName"></span>
            </td>
            <td style="width: 10%">
                <input id="Quantity" type="text" value=""  style="width: 50px" />
            </td>
            <td style="width: 10%">
                <span id="UnitPrice"></span>
            </td>
            <td style="width: 15%">
                <span id="QuantityPerUnit"></span>
            </td>
            <td style="width: 10%">
                <input id="Discount" type="text" value="" style="width: 50px" />
            </td>
            <td style="width: 15%">
                <img alt="save" title="Add item to order" onclick="AddNewLineItem()" src="@Url.Content("~/Content/Images/icon-floppy.gif")" />
            </td>
        </tr>
    </table>
</div>

<div style="padding: 20px 10px 10px 10px;">
<input id="btnAdd" type="button" onclick="ShowAddLineItem()" value="Add Line Item" />
<input id="btnOrderHeader" type="button" onclick="ShowOrderHeader()" value="Order Header" />

</div>

<div id="dialog-modal" title="Product Inquiry">
	<div id="ProductInquiryModalDiv">
    </div>
</div>

<div id="OrderDetailsData" style="visibility: hidden; display: none">
    @Html.Raw(Json.Encode(orderDetails));
</div>

@Html.Hidden("OrderID", Model.Order.OrderID)

<!--====== Template ======-->
<script type="text/html" id="OrderDetailTemplate">    

 <tr data-bind="style: { background: viewModel.SetBackgroundColor($data) }">
 
  <td style="height:25px"><div data-bind="text: ProductID"></div></td>
  <td><div data-bind="text: ProductName "></div></td>
  <td>
        <div data-bind="text: Quantity, visible: DisplayMode "></div>
        <div data-bind="visible: EditMode" >                   
            <input type="text" data-bind="value: Quantity" style="width: 50px"  />               
        </div>     
  </td>

  <td><div data-bind="text: UnitPrice"></div></td>
  <td><div data-bind="text: QuantityPerUnit "></div></td>
  <td>
        <div data-bind="text: Discount, visible: DisplayMode "></div>
        <div data-bind="visible: EditMode" >                   
            <input type="text" data-bind="value: Discount" style="width:50px" />               
        </div>     
  </td>
  <td>
        <div data-bind="visible: DisplayDeleteEditButtons">
            <div style="width:25px; float:left"><img alt="delete" data-bind="click: function() { viewModel.DeleteLineItem($data) }"   title="Delete item" src="@Url.Content("~/Content/Images/icon-delete.gif")" /></div>
            <div style="width:25px; float:left"><img alt="edit" data-bind="click: function() { viewModel.EditLineItem($data) }" title="Edit item" src="@Url.Content("~/Content/Images/icon-pencil.gif")" /></div>
        </div>
         <div data-bind="visible: DisplayCancelSaveButtons">
            <div style="width:25px; float:left"><img alt="save"  data-bind="click: function() { viewModel.UpdateLineItem($data) }" title="Save item" src="@Url.Content("~/Content/Images/icon-floppy.gif")" /></div>
            <div style="width:25px; float:left"><img alt="cancel edit" data-bind="click: function() { viewModel.CancelLineItem($data) }" title="Cancel Edit" src="@Url.Content("~/Content/Images/icon-pencil-x.gif")" /></div>
        </div>
  </td>
  </tr>

</script>

<div id="MessageBox" data-bind="html: MessageBox"></div> 

<div id="DeleteConfirmation" style="visibility:hidden">
<div id="dialog-confirm" title="Delete Confirmation?">
	<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>
    <span id="DeleteConfirmationText"></span>
    </p>     
</div>
</div>


<form id="OrderEdit" method="post" action="/Orders/OrderEdit">
    <input id="OrderID" name="OrderID" type="hidden" /> 
</form>

<script language="javascript" type="text/javascript">

    initialLineItems = jsonParse($("#OrderDetailsData").text());
    $("#ProductInquiryModalDiv").html("");

    var lineItemDisplay = function () {

        this.ProductID;
        this.ProductName;
        this.Quantity;
        this.UnitPrice;
        this.QuantityPerUnit;
        this.Discount;
        this.OriginalQuantity;
        this.OriginalDiscount;
        this.EditMode;
        this.DisplayMode;
        this.DisplayDeleteEditButtons;
        this.DisplayCancelSaveButtons;
    };

    // Overall viewmodel for this screen, along with initial state
    var viewModel = {

        LineItems: ko.observableArray(),
        MessageBox: ko.observable(),
        AddNewLineItem: ko.observable(false),

        SetBackgroundColor: function (currentLineItemData) {
            var rowIndex = this.LineItems.indexOf(currentLineItemData);
            var colorCode = rowIndex % 2 == 0 ? "White" : "WhiteSmoke";
            return colorCode;
        },

        EditLineItem: function (currentLineItemData) {

            var currentLineItem = this.LineItems.indexOf(currentLineItemData);

            this.LineItems()[currentLineItem].DisplayMode(false);
            this.LineItems()[currentLineItem].EditMode(true);
            this.LineItems()[currentLineItem].DisplayDeleteEditButtons(false);
            this.LineItems()[currentLineItem].DisplayCancelSaveButtons(true);

        },

        DeleteLineItem: function (currentLineItemData) {

            var currentLineItem = this.LineItems.indexOf(currentLineItemData);

            var productName = this.LineItems()[currentLineItem].ProductName();
            var productID = this.LineItems()[currentLineItem].ProductID();

            ConfirmDeleteLineItem(productID, productName, currentLineItem);

        },

        DeleteLineItemConfirmed: function (currentLineItem) {
            var row = this.LineItems()[currentLineItem];
            this.LineItems.remove(row);
        },

        CancelLineItem: function (currentLineItemData) {

            currentLineItem = this.LineItems.indexOf(currentLineItemData);

            this.LineItems()[currentLineItem].DisplayMode(true);
            this.LineItems()[currentLineItem].EditMode(false);
            this.LineItems()[currentLineItem].DisplayDeleteEditButtons(true);
            this.LineItems()[currentLineItem].DisplayCancelSaveButtons(false);

            this.LineItems()[currentLineItem].Quantity(this.LineItems()[currentLineItem].OriginalQuantity());
            this.LineItems()[currentLineItem].Discount(this.LineItems()[currentLineItem].OriginalDiscount());

        },

        UpdateLineItem: function (currentLineItemData) {
            currentLineItem = this.LineItems.indexOf(currentLineItemData);
            var lineItem = this.LineItems()[currentLineItem];
            UpdateOrderDetail(lineItem, currentLineItem);
        },

        UpdateOrderDetailComplete: function (currentLineItem, discount) {

            this.LineItems()[currentLineItem].DisplayMode(true);
            this.LineItems()[currentLineItem].EditMode(false);
            this.LineItems()[currentLineItem].DisplayDeleteEditButtons(true);
            this.LineItems()[currentLineItem].DisplayCancelSaveButtons(false);

            this.LineItems()[currentLineItem].OriginalQuantity(this.LineItems()[currentLineItem].Quantity());
            this.LineItems()[currentLineItem].OriginalDiscount(discount);
            this.LineItems()[currentLineItem].Discount(discount);

        }

    }

    ko.applyBindings(viewModel);

    for (i = 0; i < initialLineItems.length; i++) {
        var newLineItem = CreateLineItem(initialLineItems[i]);
        viewModel.LineItems.push(newLineItem);
    }

    function CreateLineItem(LineItem) {

        var lineItem = new lineItemDisplay();

        lineItem.ProductID = ko.observable(LineItem.ProductID);
        lineItem.ProductName = ko.observable(LineItem.ProductName);
        lineItem.Quantity = ko.observable(LineItem.Quantity);
        lineItem.OriginalQuantity = ko.observable(LineItem.Quantity);
        lineItem.OriginalDiscount = ko.observable(LineItem.Discount);
        lineItem.UnitPrice = ko.observable(LineItem.UnitPrice);
        lineItem.QuantityPerUnit = ko.observable(LineItem.QuantityPerUnit);
        lineItem.Discount = ko.observable(LineItem.Discount);
        lineItem.BackgroundColor = ko.observable(LineItem.BackgroundColor);
        lineItem.EditMode = ko.observable(false);
        lineItem.DisplayMode = ko.observable(true);
        lineItem.DisplayDeleteEditButtons = ko.observable(true);
        lineItem.DisplayCancelSaveButtons = ko.observable(false);

        return lineItem;

    }

    function ShowProductInquiryModal() {
        var url = "/Products/BeginProductInquiry";

        $.post(url, null, function (html, textStatus) {
            ShowProductInquiryModalComplete(html);
        });
    }

    function ShowProductInquiryModalComplete(productInquiryHtml) {
        $("#ProductInquiryModalDiv").html(productInquiryHtml);
        $("#dialog-modal").dialog({
            height: 500,
            width: 900,
            modal: true
        });
        setTimeout("ProductInquiryInitializeGrid()", 1000);
    }

    function GetProductInformation(productID) {

        $("#dialog-modal").dialog('close');
        $("#ProductInquiryModalDiv").html("");

        var ProductLookup = function () {
            this.ProductID;
        };

        var productLookup = new ProductLookup();
        productLookup.ProductID = productID;

        var url = "/Products/GetProductInformation";

        $.post(url, productLookup, function (results, textStatus) {
            GetProductInformationComplete(results);
        });
    }

    function GetProductInformationComplete(results) {

        if (results.ReturnStatus == true) {
            $("#ProductID").val(results.ViewModel.Product.ProductIDFormatted);
            $("#ProductName").html(results.ViewModel.Product.ProductName);
            $("#QuantityPerUnit").html(results.ViewModel.Product.QuantityPerUnit);
            $("#UnitPrice").html(results.ViewModel.Product.UnitPriceFormatted);
            $("#Quantity").focus();
        }
        else {
            viewModel.MessageBox(results.MessageBoxView);
        }
    }

    function LineItem() {
        this.OrderID;
        this.ProductID;
        this.Quantity;
        this.Discount;
        this.RowIndex;
    };

    function ShowAddLineItem() {
        viewModel.AddNewLineItem(true);
    }

    function AddNewLineItem() {
        var newLineItem = new LineItem();
        newLineItem.OrderID = $("#OrderID").val();
        newLineItem.ProductID = $("#ProductID").val();
        newLineItem.Quantity = $("#Quantity").val();
        newLineItem.Discount = $("#Discount").val();

        var url = "/Orders/AddOrderDetailLineItem";

        $.post(url, newLineItem, function (results, textStatus) {
            AddNewLineItemComplete(results);
        });

    }

    function AddNewLineItemComplete(results) {
        if (results.ReturnStatus == true) {

            var lineItem = new lineItemDisplay();

            lineItem.ProductID = results.ViewModel.OrderLineItem.OrderDetails.ProductIDFormatted;
            lineItem.ProductName = results.ViewModel.OrderLineItem.Products.ProductName;
            lineItem.Quantity = results.ViewModel.OrderLineItem.OrderDetails.Quantity;
            lineItem.UnitPrice = results.ViewModel.OrderLineItem.OrderDetails.UnitPriceFormatted;
            lineItem.QuantityPerUnit = results.ViewModel.OrderLineItem.Products.QuantityPerUnit;
            lineItem.Discount = results.ViewModel.OrderLineItem.OrderDetails.DiscountFormatted;

            var newLineItem = CreateLineItem(lineItem);
            viewModel.LineItems.push(newLineItem);

            $("#ProductID").val("");
            $("#ProductName").html("");
            $("#Quantity").val("");
            $("#Discount").val("");
            $("#UnitPrice").html("");
            $("#QuantityPerUnit").html("");

        }

        viewModel.MessageBox(results.MessageBoxView);

    }

    function UpdateOrderDetail(currentLineItem, rowIndex) {

        var updateLineItem = new LineItem();

        updateLineItem.OrderID = $("#OrderID").val();
        updateLineItem.ProductID = currentLineItem.ProductID(),
        updateLineItem.Quantity = currentLineItem.Quantity();
        updateLineItem.Discount = currentLineItem.Discount();
        updateLineItem.RowIndex = rowIndex;

        var url = "/Orders/UpdateOrderDetailLineItem";

        $.post(url, updateLineItem, function (results, textStatus) {
            UpdateOrderDetailComplete(results);
        });

    }

    function UpdateOrderDetailComplete(results) {
        if (results.ReturnStatus == true) {
            discount = results.ViewModel.OrderLineItem.OrderDetails.DiscountFormatted;
            viewModel.UpdateOrderDetailComplete(results.RowIndex, discount);
        }

        viewModel.MessageBox(results.MessageBoxView);

    }


    function ConfirmDeleteLineItem(productID, productName, currentLineItem) {

        $("#DeleteConfirmationText").html("Are you sure you want to delete <b>" + productID + " - " + productName + "</b> from this order?");

        $("#dialog-confirm").dialog({
            resizable: false,
            height: 200,
            width: 600,
            modal: true,
            buttons: {
                "Delete line item": function () {
                    DeleteLineItem(productID, productName, currentLineItem);
                    $(this).dialog("close");
                },
                Cancel: function () {
                    $(this).dialog("close");
                }
            }
        });

    }

    function DeleteLineItem(productID, productName, currentLineItem) {

        var orderID = $("#OrderID").val();

        var postData = {
            OrderID: orderID,
            ProductID: productID,
            ProductName: productName,
            RowIndex: currentLineItem
        };

        var url = "/Orders/DeleteOrderDetailLineItem";

        $.post(url, postData, function (data, textStatus) {
            DeleteLineItemComplete(data, textStatus);
        });

    }

    function DeleteLineItemComplete(results) {

        viewModel.MessageBox(results.MessageBoxView);

        if (results.ReturnStatus == true) {
            viewModel.DeleteLineItemConfirmed(results.RowIndex);
        }

    }


    function ShowOrderHeader() {
        $("#OrderEdit #OrderID").val($("#OrderID").val());
        $("#OrderEdit").submit();
    }

    function ProductIDEntered(element, e) {

        var key;

        if (window.event)
            key = window.event.keyCode;     //IE
        else
            key = e.which;     //firefox

        if (key == 13) {
            viewModel.MessageBox("");
            event.keyCode = 0;
            var productID = $("#ProductID").val();
            GetProductInformation(productID) 
        }

    }

   

</script>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Joey Software Solutions
United States United States
Mark Caplin has specialized in Information Technology solutions for the past 30 years. Specializing in full life-cycle development projects for both enterprise-wide systems and Internet/Intranet based solutions.

For the past fifteen years, Mark has specialized in the Microsoft .NET framework using C# as his tool of choice. For the past four years Mark has been implementing Single Page Applications using the Angular platform.

When not coding, Mark enjoys playing tennis, listening to U2 music, watching Miami Dolphins football and watching movies in Blu-Ray technology.

In between all this, his wife of over 25 years, feeds him well with some great home cooked meals.

You can contact Mark at mark.caplin@gmail.com

...

Comments and Discussions