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>&copy; @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());

 

posted on 2016-02-02 13:46  小呀么小二郎  阅读(275)  评论(0编辑  收藏  举报

导航