65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Knockout 和 ASP.NET 的单页应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (13投票s)

2012年6月25日

CPOL

2分钟阅读

viewsIcon

57651

如何使用 Knockout 和 ASP.NET 创建一个示例单页应用程序。

引言

在本文中,我将讨论如何使用 Knockout 和 ASP.NET 创建一个示例单页应用程序。该应用程序会根据用户类型(例如用户、管理员用户、超级管理员用户)显示用户列表,并在选择用户时显示用户详细信息。

构建此应用程序需要具备 Knockout、SAMMY 库、WCF RESTful 服务和 JavaScript 的基本知识。

在开始本文之前,让我们先了解一下 Knockout 的标准定义。

Knockout 是一个 JavaScript 库,可帮助您创建丰富、响应迅速的显示和编辑器用户界面,并具有干净的基础数据模型。 只要您有 UI 部分会动态更新(例如,根据用户操作或外部数据源更改而更改),KO 都可以帮助您更简单、更易于维护地实现它。

Sammy.js 是一个出色的轻量级路由 JavaScript 库。 您可以执行类似的操作,以便与 Knockout 配对使用(来自教程网站或 KnockoutJS)。

  1. 创建 WCF RESTful 服务
  2. 创建 ASP.NET 网站
  3. 定义 ViewModel
  4. 定义 View
  5. 定义样式
  6. 输出屏幕

创建 WCF RESTful 服务

我们将定义两种方法

  • GetUsers:根据用户类型返回用户列表
  • GetUserData:根据用户 ID 和用户类型返回用户列表

这些服务将返回一个 JSON 对象。 代码如下所示。 要了解有关如何构建 RESTful 服务的更多信息,请参阅我的文章:https://codeproject.org.cn/Articles/275279/Developing-WCF-Restful-Services-with-GET-and-POST

我已将我的服务定义为 ServiceOne.svc,因此我的接口将类似于这样

[ServiceContract]
public interface IServiceOne
{
    [OperationContract(Name = "GetUsers")]
    [WebInvoke(Method = "GET", UriTemplate = "GetUsers/utypeid/{userTypeId}", 
       RequestFormat = WebMessageFormat.Json,
       ResponseFormat = WebMessageFormat.Json)]

    myCustomObject GetUsers(string userTypeId);

    [OperationContract(Name = "GetUserData")]
    [WebInvoke(Method = "GET", UriTemplate = "GetUserData/utypeid/{userTypeId}/uid/{userId}", 
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json)]
    users GetUserData(string userTypeId, string userid);
}

此外,为了进行此操作,我定义了我的用户类 myCustomObject

这是定义

public class myCustomObject
{
    public string userTypeId { get; set; }
    public List<users> users { get; set; }
}

public class users
{
    public string userid { get; set; }
    public string usertypeid { get; set; }
    public string firstname { get; set; }
    public string lastname { get; set; }
    public string address { get; set; }
}

以下是接口中定义的方法的实现。 我避免调用数据库,因为这是一个示例,并且我定义了静态值

public class ServiceOne : IServiceOne
{
    public myCustomObject GetUsers(string userTypeId)
    {
        myCustomObject myObj = new myCustomObject();

        myObj.userTypeId = userTypeId;
        List<users> usersList = new List<users>();

        if (userTypeId == "Users")
        {
            usersList.Add(new users() { usertypeid = "Users", userid = "1", 
              firstname = "user fname 1", lastname = "user lname 1", address = "user address 1" });
            usersList.Add(new users() { usertypeid = "Users", userid = "2", 
              firstname = "user fname 2", lastname = "user lname 2", address = "user address 2" });
      
        }
        else if (userTypeId == "AdminUsers")
        {
            usersList.Add(new users() { usertypeid = "AdminUsers", userid = "3", 
              firstname = "admin user fname 1", lastname = "admin user lname 1", 
              address = "admin user address 1" });
            usersList.Add(new users() { usertypeid = "AdminUsers", userid = "4", 
              firstname = "admin user fname 2", lastname = "admin user lname 2", 
              address = "admin user address 2" });
      
        }
        else if (userTypeId == "SuperAdminUsers")
        {
            usersList.Add(new users() { usertypeid = "SuperAdminUsers", userid = "5", 
              firstname = "super admin user fname 1", lastname = "super admin user lname 1", 
              address = "super admin user address 1" });
            usersList.Add(new users() { usertypeid = "SuperAdminUsers", userid = "6", 
              firstname = "super admin user fname 2", lastname = "super admin user lname 2", 
              address = "super admin user address 2" });
      
        }

        myObj.users = usersList;

        return myObj;
    }

    public users GetUserData(string userTypeId, string userid)
    {
        List<users> usersList = new List<users>();
        usersList.Add(new users() { usertypeid = "Users", userid = "1", 
          firstname = "user fname 1", lastname = "user lname 1", address = "user address 1" });
        usersList.Add(new users() { usertypeid = "Users", userid = "2", 
          firstname = "user fname 2", lastname = "user lname 2", address = "user address 2" });
        usersList.Add(new users() { usertypeid = "AdminUsers", userid = "3", 
          firstname = "admin user fname 1", lastname = "admin user lname 1", 
          address = "admin user address 1" });
        usersList.Add(new users() { usertypeid = "AdminUsers", userid = "4", 
          firstname = "admin user fname 2", lastname = "admin user lname 2", 
          address = "admin user address 2" });
        usersList.Add(new users() { usertypeid = "SuperAdminUsers", userid = "5", 
          firstname = "super admin user fname 1", lastname = "super admin user lname 1", 
          address = "super admin user address 1" });
        usersList.Add(new users() { usertypeid = "SuperAdminUsers", userid = "6", 
          firstname = "super admin user fname 2", lastname = "super admin user lname 2", 
          address = "super admin user address 2" });
        
        users obj = (from user in usersList.Where(val => val.userid == 
                     userid && val.usertypeid == userTypeId)
                     select user).First();

        return obj;

    }
}

将您的服务托管在 IIS 中,并使用 URL 进行测试,在我的情况下如下:https:///wcfrestservice/ServiceOne.svc/GetUsers/id/Users

单击它后,您应该能够看到 JSON 对象。 确保您不使用端口号运行应用程序。

创建 ASP.NET 网站

创建一个 ASP.NET 网站,并添加一个名为 userslist.aspx 的新网页。

到目前为止,您应该已经理解了 KnockOut 遵循 MVVM 模式,即模型、视图、ViewModel 模式。

将以下脚本引用添加到您的页面

<!-- JQUERY  Library -->
<script type="text/javascript" src="http://learn.knockoutjs.com/Scripts/CDNHosted/jquery-1.7.1.min.js"></script>

<!-- KNOCKOUT  Library -->
<script type="text/javascript" src="http://learn.knockoutjs.com/Scripts/Lib/knockout-2.1.0.js"></script>

<!-- SAMMY Library -->
<script src="http://learn.knockoutjs.com/scripts/lib/sammy.js" type="text/javascript"></script>

定义 ViewModel

让我们将以下脚本代码添加到页面,请参阅内联代码注释

<script type="text/javascript">
function UsersViewModel() {
  // Data
  var self = this;

  // defines usertypes
  self.userTypes = ['Users', 'AdminUsers', 'SuperAdminUsers'];

  // holds usertypeid, when usertype is selected
  self.chosenuserTypeId = ko.observable();

  // holds list of users for a selected usertype
  self.chosenUserTypesData = ko.observable();

  // hold userdata for a selected user
  self.chosenUserData = ko.observable();

         // Behaviours    
  self.goToUserType = function (usertypeid) { location.hash = usertypeid };
  self.goToUserData = function (userdata) { location.hash = userdata.usertypeid + '/' + userdata.userid };

  // Client-side routes, can be defined by using SAMMY library
  Sammy(function () {
      
      // below segment is used to fetch userslist based on usertypeid
      this.get('#:usertypeid', function () {
            
          // sets selected usertypeid
          self.chosenuserTypeId(this.params.usertypeid);

          // destroy userdata, because at this point we need to have userslist instead of userdata
          self.chosenUserData(null);

          // make a call to wcf restful service, to fetchs users based on usertypeid
          // response from service would contains "userslist", which is filled
          // into chosenUserTypesData, whenever we statement "self.chosenUserTypesData"
          $.get("https:///wcfmailservice/ServiceOne.svc/GetUsers/Id/" + 
                this.params.usertypeid, self.chosenUserTypesData);
          

      });

      // below segment is used to fetch userdata based on usertypeid & userid
      this.get('#:usertypeid/:userid', function () {

          // sets selected usertypeid
          self.chosenuserTypeId(this.params.usertypeid);

          // destroys userslist, because at this point we need to have userdata instead of userslist
          self.chosenUserTypesData(null);

          // make a call to wcf service, to fetch user data based on usertypeid & userid
          $.get("https:///wcfmailservice/ServiceOne.svc/GetUserData/utypeid/" + 
            this.params.usertypeid + "/uid/" + this.params.userid, self.chosenUserData);
      });

      // when page load happens show default data, using sammy routing mechanism
      this.get('', function () { this.app.runRoute('get', '#Users') });


  }).run();

};

// allows to bind viewmodel defined above 
ko.applyBindings(new UsersViewModel());

</script>

定义 View

<!-- Users Types, to show list of usertypes based on usertype defined -->
<!-- foreach allows to iterate through the usertypes declared -->

<ul class="userTypes" data-bind="foreach: userTypes">
    <li data-bind="text: $data, 
                   css: { selected: $data == $root.chosenuserTypeId() },
                   click: $root.goToUserType"></li>

</ul>

 <!-- Users Grid: with keyword allows below section to be 
          shown only when data in chosenUserTypesData is available -->
<table class="userslist" data-bind="with: chosenUserTypesData">
    <thead><tr><th>First Name</th><th>Last Name</th><th>Address</th></tr></thead>
    <tbody data-bind="foreach: users">
        <tr data-bind="click: $root.goToUserData">
            <td data-bind="text: firstname"></td>
            <td data-bind="text: lastname"></td>
            <td data-bind="text: address"></td>
        </tr>     
    </tbody>
</table>

<!-- Selected User: with key allows to below section to be shown only when chosenUserData  is available-->
<div class="userData" data-bind="with: chosenUserData">
    <div class="userdetails">
        <p><label>First Name</label>: <span data-bind="text: firstname"></span></p>
        <p><label>Last Name</label>: <span data-bind="text: lastname"></span></p>
        
    </div>
</div>

定义样式

.userTypes { background-color: #bbb; list-style-type: none; padding: 0; margin: 0; border-radius: 7px; 
       background-image: -webkit-gradient(linear, left top, left bottom, 
          color-stop(0, #d6d6d6), color-stop(0.4, #c0c0c0), color-stop(1,#a4a4a4)); 
       margin: 10px 0 16px 0;
       font-size: 0px;
}
.userTypes li:hover { background-color: #ddd; }    
.userTypes li:first-child { border-left: none; border-radius: 7px 0 0 7px; }
.userTypes li { font-size: 16px; font-weight: bold; display: inline-block; padding: 0.5em 1.5em; 
           cursor: pointer; color: #444; text-shadow: #f7f7f7 0 1px 1px; 
           border-left: 1px solid #ddd; border-right: 1px solid #888; }
.userTypes li { *display: inline !important; } /* IE7 only */
.userTypes .selected { background-color: #444 !important; color: white; 
           text-shadow:none; border-right-color: #aaa; border-left: none; 
           box-shadow:inset 1px 2px 6px #070707; }    

.userslist { width: 100%; table-layout:fixed; border-spacing: 0; }
.userslist thead { background-color: #bbb; font-weight: bold; color: #444; text-shadow: #f7f7f7 0 1px 1px; }
.userslist tbody tr:hover { cursor: pointer; background-color: #68c !important; color: White; }
.userslist th, .userslist td { text-align:left; padding: 0.4em 0.3em; 
           white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.userslist th { border-left: 1px solid #ddd; border-right: 1px solid #888; padding: 0.4em 0 0.3em 0.7em; }    
.userslist th:nth-child(1), .userslist td:nth-child(1) { width: 20%; }
.userslist th:nth-child(2), .userslist td:nth-child(2) { width: 15%; }
.userslist th:nth-child(3), .userslist td:nth-child(3) { width: 45%; }
.userslist th:nth-child(4), .userslist td:nth-child(4) { width: 15%; }
.userslist th:last-child { border-right: none }
.userslist tr:nth-child(even) { background-color: #EEE; }
  
.userData .userdetails { background-color: #dae0e8; padding: 1em 1em 0.5em 1.25em; border-radius: 1em; }
.userData .userdetails h1 { margin-top: 0.2em; font-size: 130%; }
.userData .userdetails label { color: #777; font-weight: bold; min-width: 2.75em; text-align:right; display: inline-block; }
.userData .message { padding: 0 1.25em; }

输出屏幕

编码愉快… 希望这有帮助!

© . All rights reserved.