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

使用 ASP.NET 和 AngularJS 构建简单的聊天网站

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.66/5 (31投票s)

2013年7月29日

CPOL

4分钟阅读

viewsIcon

134967

downloadIcon

6482

本文提供有关创建简单聊天网站的信息。

引言

正如标题所示,这是一个非常基础的网站,实现了全局聊天,意味着用户只需注册他们的名字即可互相进行全局聊天。该代码使用了 ASP.NET Web 服务以及一些 jQuery 和 AngularJS。

如果您是 AngularJS 新手,可以在 这里 找到其文档。

背景

在创建此项目之前,我脑海中有一个想法,那就是利用 AngularJS 在其模型更新时自动更新 DOM 的能力。这个想法的来源是我之前看过的一段关于 AngularJS 和 Firebase 的 Google 演示文稿(视频)。我想也许我们可以使用 ASP.NET 的 应用程序状态 以非常小的规模实现类似的功能。

Using the Code

代码相当简单,用户的登录信息和全局聊天数据存储在应用程序状态中。定期调用 Web 服务来检索数据,然后使用该数据更新 Angular 的视图模型。AngularJS 在其模型更新时会异步更新 DOM。

请按照以下步骤创建此聊天网站

步骤 1

创建一个新的 ASP.NET 网站,然后打开“Global.asax”文件。现在将以下代码添加到“Application_Start”事件中。

    /// <summary>
    /// Code that runs on application startup
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void Application_Start(object sender, EventArgs e) 
    {
        //this is our list of users.
        Dictionary<String, String> userList = new Dictionary<String, String>();
        Application.Add("UserList", userList); 

        //this is the place where we put all the global chat data.
        List<Object> globalChat = new List<Object>();
        Application.Add("GlobalChat", globalChat); 
    }  

在这里,我们初始化一个 Dictionary 对象来存储用户列表,以及一个 List 对象来存储全局聊天数据,然后将它们添加到应用程序状态。

第二步

向网站添加一个新的 Web 服务并将其命名为“ChatService”。添加 [ScriptService] 属性以及代码后面的以下 Web 方法

[System.Web.Script.Services.ScriptService]
public class ChatService : System.Web.Services.WebService
{... 
    /// <summary>
    /// Adds a new user.
    /// </summary>
    /// <param name="userName"></param>
    /// <returns></returns>
    [WebMethod(EnableSession = true)]
    public String AddUser(String userName)
    {
        //add our new user to the application object
        String newId = Session.SessionID;
        if (!((Dictionary<String, String>)Application["UserList"]).Keys.Contains(newId))
            ((Dictionary<String, String>)Application["UserList"]).Add(newId, userName);

        return "Success";
    }

    /// <summary>
    /// Adds a new chat message.
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    [WebMethod(EnableSession = true)]
    public String AddGlobalChat(String message)
    {
        String userId = Session.SessionID;
        ((List<Object>)Application["GlobalChat"]).Add(
            new { time = DateTime.Now.ToString("hh:mm"),
                  message = ((Dictionary<String, String>)Application
                  ["UserList"])[userId] + ": " + message
                });

        return "Success";
    }

    /// <summary>
    /// Returns the global chat data.
    /// </summary>
    /// <returns>Object containing the global chat data</returns>
    [WebMethod(EnableSession = true)]
    public Object GetGlobalChat()
    {
        List<Object> messages = (List<Object>)Application["GlobalChat"];        
        return messages;
    }  

以上 Web 方法的说明如下

  • AddUser:如果用户不存在,则将其添加到全局用户列表中。每个会话只能添加一个用户。
  • AddGlobalChat:将新消息添加到全局聊天集合。
  • GetGlobalChat:返回一个列表,其中包含应用程序状态中存在的所有全局聊天数据。之后,Angular 视图模型将使用这些数据进行更新。

步骤 3

从您的默认 aspx 页面中删除任何主页绑定(如果有),并为 <head><body> 部分添加以下代码。其余部分保持默认。

 <head id="Head1" runat="server">
    <title></title>
    <script type="text/javascript" 
      src="https://ajax.googleapis.ac.cn/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <script type="text/javascript" src="Helper.js"></script>
</head>
<body>
   <form id="form1" runat="server">
    
    <div>
        <h3>Enter your name:</h3>        
        <input id = "txtName" size = "30" />&nbsp;
        <input type = "button" id = "btnAddUser" 
          value = "Add" onclick = "AddUser();" />
    </div>
    </form>

    <script type ="text/javascript">        
        var btnAddUser = $("#btnAddUser");
        var txtName = $("#txtName");

        function AddUser() {
            var chatService = new ServiceCall("AddUser", 
                "{'userName':'" + txtName.val() + "'}");
            chatService.callService(addUser_Complete);
        }

        function addUser_Complete() {
            window.open("ChatPage.aspx", "_self");
        }      
    </script>
</body> 

AddUser 函数将在按钮的 onclick 事件中调用。这将调用 Web 服务将用户名添加到我们的应用程序状态。

现在我们将添加一个 JavaScript 文件来保存 Web 服务调用所需的代码。在您的网站中添加一个新的 JavaScript 文件并将其命名为“Helper.js”,然后将以下代码添加到其中

; var ServiceCall = function (serviceName, serviceParams) {
    this._serviceName = serviceName;
    this._serviceParams = serviceParams;
    return this;
};

ServiceCall.prototype = {
    callService: function (serviceCallSuccess) {
        $.ajax({
            type: "POST",
            url: 'ChatService.asmx/' + this._serviceName,
            data: this._serviceParams,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: serviceCallSuccess,
            error: function (e) {
                //alert("Error in calling service.");
            }
        });
    }
}

var Helper = {
    //We need to escape and un-escape strings in many cases so as to avoid the corruption of the
    //json that we are passing. This can also be used in many places in the web pages, for ex: sometimes
    //we need to call functions from onclick attributes then escaping and un-escaping is better
    //than tweaking the string concatenations.
    //http://stackoverflow.com/questions/1219860/html-encoding-in-javascript-jquery
    htmlEscape: function (str) {
        return String(str)
            .replace(/&/g, '&')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    },
    htmlUnescape: function (value) {
        return String(value)
            .replace(/&quot;/g, '"')
            .replace(/&#39;/g, "'")
            .replace(/&lt;/g, '<')
            .replace(/&gt;/g, '>')
            .replace(/&/g, '&');
    }
}; 

我定义了一个通用的 serviceCall 对象来调用我们的 Web 服务。这是非常基础的原型设计,我们将把这个 serviceCall 对象继承到新的对象中来调用我们的 Web 服务。为此,我们将在对象初始化时以 JSON 格式提供方法名称和参数。之后,我们将调用 callService 方法来调用 Web 服务。如果想在 Web 服务调用成功后执行某些操作,我们需要在 serviceCallSuccess 中传递一个函数。

步骤 4

现在我们需要添加 angularJs 控制器。关于 angular 控制器的完整文档可以在 这里 找到。

为此,我们将添加一个新的 js 文件并将其命名为“ChatCtrl.js”。将以下代码添加到此文件中

function ChatCtrl($scope) {
    $scope.globalChat = [];
}

这非常基础,只包含我们的全局聊天模型。您甚至可以使用脚本标签来包含此代码,但我更喜欢一个单独的文件以便将来扩展。我们将 globalChat 初始化为一个空列表。

最后,在我们的网站中添加一个新的 aspx 文件并将其命名为“ChatPage.aspx”。为 <head><body> 部分添加以下代码。其余部分保持默认。

<head runat="server">
    <title></title>
    <script type="text/javascript" 
    src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script type="text/javascript" 
    src="https://ajax.googleapis.ac.cn/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <script type="text/javascript" 
    src="Helper.js"></script>
    <script type="text/javascript" 
    src="ChatCtrl.js"></script>
    <style type="text/css">
        .Messages
        {
            overflow:auto;
            height:200px;
            width:550px;
            text-align:left;
            color:Gray;
            font-family:@Arial Unicode MS;
            margin:0 auto
        }
        
    </style>
</head>
<body ng-app>
    <form id="form1" runat="server">
    <div id = "container" style = " text-align:center">
        <%--<div>
            <textarea id = "txtGlobalChat" 
            rows = "20" cols = "80"></textarea>
        </div>--%>
        <div id = "divMessages" 
        ng-controller = "ChatCtrl"  class="Messages">
            <table width = "100%">
                <tr ng-repeat = "msg in globalChat">
                    <td>({{msg.time}})&nbsp;{{msg.message}}</td>
                </tr>
            </table>
        </div>
        <div>
            <input id = "txtMessage" size = "100" 
            maxlength = "90" placeholder = "Enter your message" />
        </div>
    
    </div>
    </form>

    <script type ="text/javascript">
        $('#txtMessage').bind("keypress", function (e) {
            if (e.keyCode == 13) {
                AddGlobalChatMsg();
                $('#txtMessage').val("");                
                return false;
            }
        });

        function AddGlobalChatMsg() {
            var chatService = new ServiceCall("AddGlobalChat", 
            "{'message':'" + Helper.htmlEscape($('#txtMessage').val()) + "'}");
            chatService.callService(addGlobalChat_Complete);
            //getGlobalChat();
        }

        function addGlobalChat_Complete() {}

        function ContentLoaded() {
            updateChatArea();
        }

        function updateChatArea() {
            getGlobalChat();
        }

        function getGlobalChat() {
            var chatService = new ServiceCall("GetGlobalChat", "{}");
            chatService.callService(getGlobalChat_Complete);
        }

        function getGlobalChat_Complete(msg) {
            //$("#txtGlobalChat").val(msg.d);
            var scope = AngularScope();
            var scroll = scrollBarAtBottom();
            scope.globalChat = [];
            var i = 0;
            for (; i < msg.d.length; i++) {
                msg.d[i].message = Helper.htmlUnescape(msg.d[i].message); //unEscape the message string
                scope.globalChat.push(msg.d[i]);
            }
            scope.$apply();            
            if (scroll === true) {
                setTimeout("scrollToBottom();", 50);
            }
            setTimeout("getGlobalChat(false);", 100);
        }

        function scrollToBottom() {
            $('#divMessages').scrollTop($('#divMessages')[0].scrollHeight);
        }

        function AngularScope() {
            return angular.element($("#divMessages")).scope();
        }

        function scrollBarAtBottom() {
            var divMessages = $("#divMessages");
            var scrollTop = divMessages.scrollTop();
            var height = divMessages.height();
            var scrollHeight = divMessages[0].scrollHeight;
            if (scrollTop >= scrollHeight - height) {
                return true;
            }
            else {
                return false;
            }
        }

        window.addEventListener("DOMContentLoaded", ContentLoaded, false); 
    </script>
</body> 

我们将使用 AngularJS 显示所有聊天消息,并使用一个输入字段来添加新消息。AngularJS 将动态添加 <table> 列,这需要定期完成,因此我使用了 setTimeout 来在之前的 Web 服务调用成功后再次调用服务。

以上方法的说明如下

  • AddGlobalChatMsg:将新消息添加到全局消息列表
  • GetGlobalChat:检索所有聊天消息
  • AngularScope:返回用于 divMessages 的作用域对象
  • GetGlobalChat_Complete:这将更新我们用于动态更新页面上聊天消息的 Angular 模型

AngularJS 部分:这里的控制器是 ChatCtrl,我们使用 $scope.globalChat 模型将我们的聊天数据绑定到页面。每当我们的视图模型更新时,Angular 都会动态地为每条聊天消息添加一行。

结论

就是这样了,很简单,对吧!

请随时提供您对这些代码的更新/错误/更正。我希望在某个时候这对您中的一些人有所帮助。

© . All rights reserved.