initialLineItems = jsonParse($("#OrderDetailsData").text());
var viewModel = {
LineItems: ko.observableArray()
}
ko.applyBindings(viewModel);
for (i = 0; i < initialLineItems.length; i++) {
var newLineItem = CreateLineItem(initialLineItems[i]);
viewModel.LineItems.push(newLineItem);
}
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;
};
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;
}
Knockout映射插件
上例中,我们采取的是自定义创建View Model的方式。另一种方式是采用Knockout映射插件,选择合适的映射规则,直截了将JavaScript对象与View Model绑定。
编辑,更新与删除Template Items
完整的页面Knockout View Model包含有line item的编辑,更新,删除。
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);
}
}
选择一个line item,点击铅笔编辑图标,EditLineItem函数会触发onclick事件,line item处于编辑模式。如下:
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);
},
借助Knockout模版与Knockout绑定技术,我们可以创建类似ASP.NET Web Forms DataGrid控件的完整in-line编辑grid。
点击Add Line Item按钮,打开一个line item,可将一个item添加至order中。
使用modal popup窗口,可搜索一个Product Item。在一个新的line item上点击Search按钮,弹出product search 窗口。
The Modal Popup Product Search 窗口
Modal 弹出窗口是AJAX调用与Partial View的结合。AJAX 请求调用Product Inquiry partial view,返回product search的内容,最后填充至DIV标签。
<div id="ProductInquiryModalDiv"> </div>
</div>
Modal 弹出窗口是一个具有dialog功能的JQuery插件。
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
});
//
// execute Product Inquiry query after the initial page content has been loaded
//
setTimeout("ProductInquiryInitializeGrid()", 1000);
}
Product Inquiry Search窗口 – UID生成机制
Product Inquiry Search窗口本身是一个Partial View。由于该窗口与Order Order页面加载的DOM一样,因此所有的HTML控件与动态创建的JavaScript函数及变量均要求名字少有。在渲染页面内容之前,该Partial View实例化自定义的PageIDGeneration类,调用GenerateID方法,生成少有的控件ID,JavaScript函数名,以及变量名。PageIDGeneration类通过设置unique Guid数目,保证生成ID的唯一性。
@using NorthwindWebApplication.Helpers;
@{
NorthwindWebControls.PageIDGeneration webControls =
new NorthwindWebControls.PageIDGeneration();
string txtProductID = webControls.GenerateID("ProductID");
string txtProductDescription = webControls.GenerateID("ProductName");
string btnSearch = webControls.GenerateID("BtnSearch");
string btnReset = webControls.GenerateID("BtnReset");
string messageBox = webControls.GenerateID("MessageBox");
string productResults = webControls.GenerateID("ProductResults");
}
<div class="SearchBar">
<div style="float:left; width:200px">
Product ID
</div>
<div style="float:left; width:200px">
Product Description
</div>
<div style="clear:both;"></div>
<div style="float:left; width:200px">
<input id="@txtProductID" type="text" value="" style = "width:150px" />
</div>
<div style="float:left; width:200px ">
<input id="@txtProductDescription" type="text" value="" style = "width:150px" />
</div>
<input id="@btnSearch" type="button" value="Search" />
<input id="@btnReset" type="button" value="Reset"/>
</div>
<div style="clear:both;"></div>
<div id="@productResults"></div>
<div id="@messageBox"></div>
@Html.RenderJavascript(webControls.RenderJavascriptVariables("ProductInquiry_"))
<script language="javascript" type="text/javascript">
$(ProductInquiry_BtnSearch).click(function() {
ProductInquiryInitializeGrid();
});
$(ProductInquiry_BtnReset).click(function() {
$(ProductInquiry_ProductID).val("");
$(ProductInquiry_ProductName).val("");
ProductInquiryInitializeGrid();
});
function ProductInquiryRequest() {
this.CurrentPageNumber;
this.PageSize;
this.ProductID;
this.ProductName;
this.SortDirection;
this.SortExpression;
this.PageID;
};
function ProductInquiry(currentPageNumber, sortExpression, sortDirection) {
var url = "/Products/ProductInquiry";
var productInquiryRequest = new ProductInquiryRequest();
productInquiryRequest.ProductID = $(ProductInquiry_ProductID).val();
productInquiryRequest.ProductName = $(ProductInquiry_ProductName).val();
productInquiryRequest.CurrentPageNumber = currentPageNumber;
productInquiryRequest.SortDirection = sortDirection;
productInquiryRequest.SortExpression = sortExpression;
productInquiryRequest.PageSize = 10;
productInquiryRequest.PageID = $(ProductInquiry_PageID).val();
$.post(url, productInquiryRequest, function (data, textStatus) {
ProductInquiryComplete(data);
});
};
function ProductInquiryComplete(result) {
if (result.ReturnStatus == true) {
$(ProductInquiry_ProductResults).html("");
$(ProductInquiry_ProductResults).html(result.ProductInquiryView);
$(ProductInquiry_MessageBox).html("");
}
else {
$(ProductInquiry_MessageBox).html(result.MessageBoxView);
}
}
function ProductInquiryInitializeGrid() {
ProductInquiry(1, "ProductName", "ASC");
}
function ProductSelected(productID) {
GetProductInformation(productID);
}
</script>
总结
ASP.NET MVC是一个适用于大型Web应用开发的日益成熟的Web框架。MVC的架构思想是注重分离,对于具有Trial、Error、Discovery的Web应用开发而言,MVC的学习曲线就显得与众不同。MVC与我们过去一直使用的ASP.NET Web Forms技术与Web Form post-back model技术完全不同。在未来,MVC开发者需要更加注重新兴框架与开源库,增强型MVC的开发。
本文重点关注的是开源JavaScript库Knockout与JQuery,以及用于交换视图与控制器数据的JSON。建议MVC开发者也多多关注其它的开发工具与框架,特别是Backbone与JavaScriptMVC。作为比较,后续的文章将会在示例程序Northwind中引入Backbone与JavaScriptMVC。