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.
void Application_Start(object sender, EventArgs e)
{
Dictionary<String, String> userList = new Dictionary<String, String>();
Application.Add("UserList", userList);
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
{...
[WebMethod(EnableSession = true)]
public String AddUser(String userName)
{
String newId = Session.SessionID;
if (!((Dictionary<String, String>)Application["UserList"]).Keys.Contains(newId))
((Dictionary<String, String>)Application["UserList"]).Add(newId, userName);
return "Success";
}
[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";
}
[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" />
<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) {
}
});
}
}
var Helper = {
htmlEscape: function (str) {
return String(str)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
},
htmlUnescape: function (value) {
return String(value)
.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&/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}}) {{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">
$( if (e.keyCode == 13) {
AddGlobalChatMsg();
$( return false;
}
});
function AddGlobalChatMsg() {
var chatService = new ServiceCall("AddGlobalChat",
"{'message':'" + Helper.htmlEscape($( 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() {
$( }
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.