Click here to Skip to main content
Click here to Skip to main content

A Simple Chat Website Using ASP.NET and AngularJS

, 24 Aug 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
This article provides information to create a simple chat website.

Introduction

As the title suggests, this is a very basic website that implements global chat, meaning that the users can chat globally with each other by just registering their name. The code uses ASP.NET web services along with a little jQuery and AngularJS.

If you are new to AngularJS, its documentation can be found here.

Background

The idea that I had in mind before creating this was to utilize the capability of AngularJS to automatically update the DOM whenever its models are updated. The source of this idea is a Google presentation that I saw some time back about AngularJS and Firebase (video). I thought that maybe we can implement something like this at a very small scale using the Application State of ASP.NET.

Using the Code

The code is fairly simple, the user's information and the global chat data is stored in the application state. Periodic web service calls are made to retrieve the data and then the angular view models are updated with that data. The DOM is updated asynchronously by the angular whenever its models are updated.

Follow the steps given below to create this chat website:

Step 1

Create a new ASP.NET website, and open the 'Global.asax' file. Now add the following code to the 'Application_Start' event.

    /// <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); 
    }  

Here, we are initializing a Dictionary object to store the list of users and a List object to store the global chat data, and then adding them to the application state.

Step 2

Add a new web service to the website and name it 'ChatService'. Add the [ScriptService] attribute and the following web methods to the code behind:

[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;
    }  

Here is the description of the web methods that are given above:

  • AddUser: Adds a new user to the global user list if it does not already exist. Only one user can be added per session.
  • AddGlobalChat: Adds a new message to the global chat collection.
  • GetGlobalChat: Returns a list containing all the global chat data present in the Application State. Angular view model is then later updated with this data.

Step 3

Remove the master page bindings (if any) from your default aspx page and add the following code to it for the <head> and <body> sections. Keep the rest as default.

 <head id="Head1" runat="server">
    <title></title>
    <script type="text/javascript" 
      src="https://ajax.googleapis.com/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> 

The AddUser function will invoke on the onclick event of the button. This will call the web service to add the user name to our application state.

Now we will add a JavaScript file to hold the code required for the web service calls. Add a new JavaScipt file to your website and name it 'Helper.js' and add the following code to it:

; 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, '&amp;')
            .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(/&amp;/g, '&');
    }
}; 

I have defined a generic serviceCall object to call our web services. This is very basic prototyping, we will inherit this serviceCall object into new objects to call our web services. For this, we will provide the method name and the parameters in the JSON format at the time of object initialization. After that, we will invoke the callService method to call the web service. We need to pass a function in the serviceCallSuccess if we want to do something on the success of our web service call.

Step 4

Now we need to add the angularJs controller. The complete documentation about angular controllers can be found here.

For this, we will add a new js file and name it 'ChatCtrl.js'. Add the following code to this file:

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

This is very basic containing only our model for the global chat. You can even use a script tag for this code but I prefer a separate file for further expansions. We will initialize globalChat as an empty list.

Lastly, add a new aspx file to our website and name it 'ChatPage.aspx'. Add the following code to it for the <head> and <body> sections. Keep the rest as default.

<head runat="server">
    <title></title>
    <script type="text/javascript" 
    src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script type="text/javascript" 
    src="https://ajax.googleapis.com/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> 

We will use AngularJS to display all the chat messages and an input field to add new message. AngularJS will dynamically add <table> columns, this needs to be done periodically so I have used setTimeout to call the service again if our previous web service call is a success.

Here is the description of the methods used above:

  • AddGlobalChatMsg: Adds a new message to the global message list
  • GetGlobalChat: Retrieves all the chat messages
  • AngularScope: Returns the scope object being used for the divMessages
  • GetGlobalChat_Complete: This will update our angular model that we are using to dynamically update the chat messages to our page

AngularJS Part: Our controller here is ChatCtrl and we are using $scope.globalChat model to bind our chat data to the page. Angular will dynamically add a single row per chat message whenever our view model is updated.

Conclusion

That was all guys, simple enough right!

Feel free to provide your updates/bugs/corrections for this code. I hope this proves useful for some of you at some point or the other.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nitij
Software Developer
India India
Just a regular guy writing regular code Smile | :)
 
My Homepage
Follow on   Twitter   LinkedIn

Comments and Discussions

 
QuestionIs there a way for only push new messages? Pinmemberaberlin23-Oct-14 7:10 
QuestionChat Sample in ASP.NET Pinmemberpixel.rajeshkumar15-May-14 4:45 
AnswerRe: Chat Sample in ASP.NET PinprofessionalAfzaal Ahmad Zeeshan27-Nov-14 10:02 
GeneralMy vote of 5 PinmemberMohamed Kamal26-Apr-14 10:29 
QuestionChat PinmemberPetarS08925-Mar-14 0:18 
QuestionChat web PinmemberManish Rathod14-Feb-14 0:53 
AnswerRe: Chat web PinmemberNitij Kumar14-Feb-14 6:29 
GeneralMy vote of 5 PinprofessionalRenju Vinod4-Oct-13 0:19 
GeneralRe: My vote of 5 PinmemberNitij Kumar14-Feb-14 6:29 
QuestionAngular Js with ASP.net mvc Pinmemberdaman pal singh11-Sep-13 4:45 
BugScroll to bottom Pinmembersacad31-Aug-13 7:19 
GeneralRe: Scroll to bottom PinmemberNitij Kumar31-Aug-13 20:15 
GeneralMy vote of 5 PinmemberMember 1023406826-Aug-13 20:32 
GeneralRe: My vote of 5 PinmemberNitij Kumar31-Aug-13 20:50 
QuestionTime display error PinmemberNitinSingh15-Aug-13 18:16 
GeneralMy vote of 2 Pinmemberarhoads767-Aug-13 11:49 
GeneralRe: My vote of 2 PinmemberNitij Kumar7-Aug-13 20:54 
GeneralMy vote of 5 Pinmemberraj ch1-Aug-13 1:03 
GeneralRe: My vote of 5 PinmemberNitij Kumar2-Aug-13 8:02 
GeneralNice Explanation. Vote of 5!!!! Pinmemberparkavikarthi30-Jul-13 23:57 
GeneralRe: Nice Explanation. Vote of 5!!!! PinmemberNitij Kumar2-Aug-13 8:02 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun30-Jul-13 23:33 
GeneralRe: My vote of 5 PinmemberNitij Kumar2-Aug-13 8:01 
Questionknockout & AngularJS both are used for same purpose PinmemberTridip Bhattacharjee30-Jul-13 9:42 
GeneralRe: knockout & AngularJS both are used for same purpose PinmemberNitij Kumar30-Jul-13 20:00 
GeneralRe: knockout & AngularJS both are used for same purpose Pinmemberarhoads767-Aug-13 11:52 
QuestionScreen shots PinmemberBoipelo30-Jul-13 5:36 
QuestionI am getting an exception!! PinmemberMannava Siva Aditya30-Jul-13 3:44 
AnswerRe: I am getting an exception!! PinmemberNitij Kumar30-Jul-13 3:51 
GeneralRe: I am getting an exception!! PinmemberMannava Siva Aditya30-Jul-13 3:56 
Question[ScriptService] PinmemberMember 988248630-Jul-13 2:43 
AnswerRe: [ScriptService] PinmemberNitij Kumar30-Jul-13 3:49 
GeneralMy vote of 5 Pinmembergiri00129-Jul-13 21:53 
GeneralNice PinmemberCodeHawkz29-Jul-13 21:39 
GeneralRe: Nice PinmemberNitij Kumar29-Jul-13 23:51 
GeneralMy vote of 1 Pinmemberalvas29-Jul-13 15:23 
GeneralRe: My vote of 1 [modified] PinmemberNitij Kumar29-Jul-13 18:32 
GeneralRe: My vote of 1 Pinmemberalvas29-Jul-13 23:27 
Questionplease upload ur source code in downloadable format PinmemberTridip Bhattacharjee29-Jul-13 5:38 
BugError calling service when apostrophe is used. Pinmemberdadcox29-Jul-13 5:19 
GeneralRe: Error calling service when apostrophe is used. PinmemberNitij Kumar29-Jul-13 18:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141220.1 | Last Updated 24 Aug 2013
Article Copyright 2013 by Nitij
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid