Knockout.js, Asp.Net MVC and Bootstrap 前端设计
原文地址:http://ddmvc4.codeplex.com/
原文名称:Design and Develop a website using ASP.NET MVC 4, EF, Knockoutjs and Bootstrap
本文参照:http://www.cnblogs.com/haogj/archive/2013/06/08/3125994.html
在开始 UI 部分之前,我们先看一下在 ASP.NET MVC4 中使用 Knockoutjs 和 Bootstrap 有什么好处?
Why Knockoutjs:
Knockout 使用 JavaScript ViewModel 实现了 MVVM 模式. 在 MVC 中还有一个很棒的因素是从
Javascript 模型序列化为 Json 和从 Json 反序列为模型都很简单,在 MVC4 中已经包含了这个脚本库,
这使得在我们开发复杂的 UI 的时候,不论怎样修改,都只需要很少的编码,马上我们用它来实现页面。
Why Bootstrap:
Twitter 的 Bootstrap 是包括简单并且灵活的 HTML, CSS, 以及广受欢迎的 Javascript
用户界面组件和交互。包括一组 CSS 样式,组件和 JavaScript 插件。提供了跨平台的支持,
消除了不同平台的不一致问题。处理的非常好,良好的文档和 Twitter Bootstrap's
站点本事就是现实中很棒的参考。最后,它节省了我大量的时间,只需要很少的测试,几乎没有浏览器的问题,节约了一半的开发时间,在我们的框架中其它优点还
包括。
- 12-列表个, 固定布局, 流式布局以及响应式布局.
- 提供基本的 CSS, 包括:版式, code (使用 Google prettify 的语法高亮), 表格, 表单, 按钮,以及 Glpyhicons 图标 .
- Web UI 组件,例如 按钮, 导航菜单, 标签, 缩略图, 提示, 进度条和其他杂项.
- Javascript 插件,包括模式对话框, 下拉列表, 滚动条, 窗格, 工具提示, 弹出窗口, 提示, 按钮, 收缩, 转轮和提示.
在下面的步骤中,我们将演练使用测试数据来创建布局,设计 UI ,完成上述的目标。
Step1:
vs2013建立一个空解决方案:
Step2:
右键解决方案添加MVC项目:
Step3:
建立完项目后的目录中发现已经存在Bootstrap的样式和js文件,但是Scripts文件夹下没有konckout.js文件。所以需要添加konckout.js:
打开 工具--》库程序包管理器--》管理解决方案的 NuGet程序包,联机搜索konckout,然后安装(有时会提示““未能解析此远程名称XXXXXXXX”的错误,点开链接查看解决方案):
Step4:
将文件bootstrap和konckout.js添加到web母版页中:
App_Start/BundleConfig:添加如下代码:
bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/bootstrap.css", "~/Content/site.css")); bundles.Add(new ScriptBundle("~/bundles/knockout").Include( "~/Scripts/knockout-3.4.0.js"));
母版页_layout.cshtml代码:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - 我的KnockOutJs测试应用</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/knockout") @Scripts.Render("~/bundles/bootstrap") </head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("KnockOutJs测试应用程序", "Index", "Home", null, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("主页", "Index", "Home")</li> <li>@Html.ActionLink("关于", "About", "Home")</li> <li>@Html.ActionLink("联系方式", "Contact", "Home")</li> </ul> </div> </div> </div> <div class="container body-content"> @RenderSection("scripts", required: false) <section> @RenderBody() </section> <hr /> <footer> <p>© @DateTime.Now.Year - 我的 KnockOutJs测试应用</p> </footer> </div> </body> </html>
Step5:
在Scripts文件夹下添加‘ViewPackgeJs’用于放置视图页面js文件,新建Js文件Home_Index.js:
Home_Index.js文件中创建一个模拟的联系人数据数组, (最后我们从数据库中获取), 随后,我们使用这些数据填充表格。
var UserProfile = [ { "ProfileID": 1, "FirstName": "王", "LastName": "二小", "E_mail":"wangerxiao@sina.com" }, { "ProfileID": 2, "FirstName": "张", "LastName": "嘎", "E_mail": "wangerxiao@sina.com" }, { "ProfileID": 3, "FirstName": "吴", "LastName": "小明", "E_mail": "wangerxiao@sina.com" }, ]
然后,我们创建 UserInforViewModel, ViewModel 用来保存联系人信息,数组用来保存联系人信息的集合。注意这里使用 ko.observableArray, 相当于常规数组,是观察者模式中的主题,这意味着它可以在其中的项目发生变化的时候,自动更新界面。
最后,我们需要使用 ko.applyBindings() 来激活 knockout.
var UserInforViewModel = function () { var self = this; var refresh = function () { self.Profiles(UserProfile); }; self.Profiles = ko.observableArray([]); refresh(); }
ko.applyBindings(new UserInforViewModel())
Step6:
然后我们开始编辑Index视图页面的Html代码,以显示联系人列表。我们在 tbody 元素上使用 foreach 绑定,使用 knockout 根据联系人数组中的每一个数据生成对应的子元素,然后告诉 knockout 我们希望使用对每个数据生成 tr 来填充 tbody,并将Home_Index.js添加到页面Home/Index视图文件中(注意:一定要将js文件添加到页面最下面,保证页面加载完成后执行js)。
@{ ViewBag.Title = "Home Page"; } <div style="margin-top:10px;"> <table class="table table-striped table-bordered table-condensed"> <tr> <th>First Name</th> <th>Last Name</th> <th>E-mail</th> </tr> <tbody data-bind="foreach: Profiles"> <tr> <td data-bind="text: FirstName"></td> <td data-bind="text: LastName"></td> <td data-bind="text: E_mail"></td> </tr> </tbody> </table> </div> <script src="~/Scripts/ViewPackgeJs/Home_Index.js"></script>
Step7:
现在,我们需要为每一行增加编辑和删除功能,表格的上边有一个创建新联系人的按钮,新增和编辑采用弹出modal层的方式实现,做以下工作:
- 在模版中添加 th 和 td ,在脚本中绑定 removeProfile 函数处理按钮的点击事件处理
- 修改名字单元格,增加编辑联系人的链接,使用 editProfile 函数绑定点击事件
- 在表格的前面添加创建联系人的按钮,使用 createProfile 函数绑定到点击事件处理
页面内容如下所示 :
@{ ViewBag.Title = "Home Page"; } <div style="margin-top:10px;"> <input type="button" class="btn btn-small btn-primary" value="新增" data-bind="click:$root.AddUserInfor" /> <hr /> <table class="table table-striped table-bordered table-condensed"> <tr> <th>First Name</th> <th>Last Name</th> <th>E-mail</th> </tr> <tbody data-bind="foreach: Profiles"> <tr> <td class="name"><a style="cursor:pointer;" data-bind="text: FirstName, click: $parent.EditUserInfor"></a></td> <td data-bind="text: LastName"></td> <td data-bind="text: E_mail"></td> <td><button class="btn btn-mini btn-danger" data-bind="click: $parent.DeleteUserInfor">删除</button></td> </tr> </tbody> </table> </div> <!-- 新增体模态框(Modal) --> <div class="modal fade" id="myAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog" style="width:700px;"> <div class="modal-content" id="UserInfor"> <div class="modal-header" style="height:50px;"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"> × </button> <h4 id="myModalLabel">新增用户</h4> </div> <div class="modal-body"> <input type="hidden" name="InstrumentBodyID" id="B_InstrumentBodyID" value="" /> <table class="basicTable"> <tbody data-bind='with: profile'> <tr> <td class="td-right"><span class="no_nullspan">*</span>序号:</td> <td class="td-padding15"><input type="text" class="scinputlong" data-bind='value: ProfileID' name="serial_Num" id="serial_Num" maxlength="25" /></td> <td class="td-right"><span class="no_nullspan">*</span> First Name:</td> <td class="td-padding15"> <input type="text" class="scinputlong" data-bind='value: FirstName' name="First_Name" id="First_Name" maxlength="25" /> </td> </tr> <tr style="margin-left:10px;"> <td class="td-right"><span class="no_nullspan">*</span>Last Name:</td> <td class="td-padding15"> <input class="scselectlong" data-bind='value: LastName' name="Last_Name" id="Last_Name" /> </td> <td class="td-right"><span class="no_nullspan">*</span> E-mail:</td> <td class="td-padding15"> <input type="text" class="scinputlong" data-bind='value: E_mail' name="E-mail" id="E-mail" maxlength="19" /> </td> </tr> </tbody> </table> </div> <div class="modal-footer"> <button class="btn btn-small btn-success" data-bind='click: saveUserInfor'>保 存</button> <button class="btn btn-small btn-primary" data-dismiss="modal" aria-hidden="true" @*data-bind="click:$root.backToProfileList"*@>返 回</button> </div> </div><!-- /.modal-content --> </div><!-- /.modal --> </div> <!-- ModalEnd --> <script src="~/Scripts/ViewPackgeJs/Home_Index.js"></script>
执行效果如图:
Step8:
返回Js文件中开始添加新增(AddUserInfor),编辑(EditUserInfor),删除(DeleteUserInfor),保存(saveUserInfor)方法:
添加数据绑定:
var Profile = function (profile) { var AE_self = this; AE_self.ProfileID = ko.observable(profile ? profile.ProfileID : 0); AE_self.FirstName = ko.observable(profile ? profile.FirstName : ''); AE_self.LastName = ko.observable(profile ? profile.LastName : ''); AE_self.E_mail = ko.observable(profile ? profile.E_mail : ''); };
添加操作方法后的UserInforViewModel变成:
var UserInforViewModel = function () { var self = this; var refresh = function () { self.Profiles(UserProfile); }; self.Profiles = ko.observableArray([]); refresh(); self.profile = ko.observable(new Profile()) self.AddUserInfor = function () { self.profile(new Profile()) $("#myAddModal").modal("show"); }; self.EditUserInfor = function (UserInforModel) { self.profile(new Profile(UserInforModel)) $("#myAddModal").modal("show"); }; self.DeleteUserInfor = function () { alert("delete"); }; self.saveUserInfor = function () { alert("Date to save is : " + JSON.stringify(ko.toJS(self.profile()))); $("#myAddModal").modal("hide"); }; }
到此,前端试图页面和js部分就完成了。执行效果:
Home_Index.js文件全部代码:
var UserProfile = [ { "ProfileID": 1, "FirstName": "王", "LastName": "二小", "E_mail":"wangerxiao@sina.com" }, { "ProfileID": 2, "FirstName": "张", "LastName": "嘎", "E_mail": "wangerxiao@sina.com" }, { "ProfileID": 3, "FirstName": "吴", "LastName": "小明", "E_mail": "wangerxiao@sina.com" }, ] var UserInforViewModel = function () { var self = this; var refresh = function () { self.Profiles(UserProfile); }; self.Profiles = ko.observableArray([]); refresh(); self.profile = ko.observable(new Profile()) self.AddUserInfor = function () { self.profile(new Profile()) $("#AEUserInforModal").modal("show"); }; self.EditUserInfor = function (UserInforModel) { self.profile(new Profile(UserInforModel)) $("#AEUserInforModal").modal("show"); }; self.DeleteUserInfor = function (UserInforModel) { self.Profiles.remove(UserInforModel); alert("delete"); }; self.saveUserInfor = function () { alert("Date to save is : " + JSON.stringify(ko.toJS(self.profile()))); $("#AEUserInforModal").modal("hide"); }; } /* 编辑--start*/ var Profile = function (profile) { var AE_self = this; AE_self.ProfileID = ko.observable(profile ? profile.ProfileID : 0); AE_self.FirstName = ko.observable(profile ? profile.FirstName : ''); AE_self.LastName = ko.observable(profile ? profile.LastName : ''); AE_self.E_mail = ko.observable(profile ? profile.E_mail : ''); }; /* 编辑--end*/ ko.applyBindings(new UserInforViewModel());