ASP.NET MVC: Implementing A ShoutBox Feature Using jQuery And AJAX

This article will walk you through how to implement a simple "shoutbox" feature in your ASP.NET MVC application. I call the feature  "shoutbox" because users within your web site can exchange conversations with each other. You can think of it as a comment board or pretty much similar to a group chat window. Please keep in mind that a "shoutbox" is not a full blown implementation of a chat feature; if you are looking for a chat application then you can refer my other article about Building a Simple Real-Time Chat Application using ASP.NET SignalR.

There are many possible ways to implement this feature, but since this article is targeted for beginners to intermediate developers then I decided to use a simple and typical way of performing asynchronous operations using jQuery and AJAX. If you want a simple and clean API that allows you to create real-time web applications where the server needs to continuously push data to clients/browsers then you may want to look at ASP.NET SignalR instead.

Before you go any further make sure that have knowledge about ASP.NET MVC and how things work in MVC arch because I will not be covering those in this article. I would recommend following my previous articles about "Building Web Application Using Entity Framework and MVC 5" as I have integrated this feature in that application. If you haven’t gone through my previous articles then you can refer to the following links below:

Let's get started!

STEP 1:

The very first thing we need to do is to create a new table in the database for storing the message of each users. So go ahead and launch SQL Server Management Studio and create a Message table by running the following SQL script below:

  1. CREATE TABLE[dbo].[Message]  
  2. (  
  3.     [MessageID][int] IDENTITY(1, 1) NOT NULL
        [SYSUserID][
    intNULL
        [MessageText][
    varchar](maxNULL
        [DatePosted][datetime] 
    NULL,  
  4. CONSTRAINT[PK_Message] PRIMARY KEY CLUSTERED
       (  
  5.         [MessageID] ASC  
  6.     ) WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW      _LOCKS = ON, ALLOW_PAGE_LOCKS = ONON[PRIMARY]  
  7. ON[PRIMARY] TEXTIMAGE_ON[PRIMARY]  
  8.   
  9. GO  
STEP 2:

Now switch back to Visual Studio and then open your EF designer by going to the Models folder > DB >DemoModel.edmx.

Right-click on the designer surface and then select "Update Model from Database". Select the Message table to add it to our entity set and click finish as shown in the figure below:

table

STEP 3:

Add the following class under Models folder >ViewModel>UserModel.cs,
  1. public class UserMessage  
  2. {  
  3.     public int MessageID   
  4.     {  
  5.         get;  
  6.         set;  
  7.     }  
  8.     public int SYSUserID   
  9.     {  
  10.         get;  
  11.         set;  
  12.     }  
  13.     public string FirstName   
  14.     {  
  15.         get;  
  16.         set;  
  17.     }  
  18.     public string LastName   
  19.     {  
  20.         get;  
  21.         set;  
  22.     }  
  23.     public string MessageText   
  24.     {  
  25.         get;  
  26.         set;  
  27.     }  
  28.     public DateTime ? LogDate   
  29.     {  
  30.         get;  
  31.         set;  
  32.     }  
  33. }  
The code above is just a simple class that houses some properties to store data from the database.

STEP 4:

Add the following code block under Models folder >ObjectManager>UserManager.cs,
  1. public List < UserMessage > GetAllMessages()   
  2. {  
  3.     using(DemoDBEntitiesdb = newDemoDBEntities())  
  4.     {  
  5.         var m = (from q indb.SYSUsers join q2 indb.Messages on q.SYSUserID equals q2.SYSUserID join q3 indb.SYSUserProfiles on q.SYSUserID equals q3.SYSUserID selectnewUserMessage {  
  6.             MessageID = q2.MessageID,  
  7.                 SYSUserID = q.SYSUserID,  
  8.                 FirstName = q3.FirstName,  
  9.                 LastName = q3.LastName,  
  10.                 MessageText = q2.MessageText,  
  11.                 LogDate = q2.DatePosted  
  12.         }).OrderBy(o => o.LogDate);  
  13.   
  14.         returnm.ToList();  
  15.     }  
  16. }  
  17.   
  18. public void AddMessage(intuserID, stringmessageText)  
  19. {  
  20.     using(DemoDBEntitiesdb = newDemoDBEntities())  
  21.     {  
  22.         Message m = newMessage();  
  23.         m.MessageText = messageText;  
  24.         m.SYSUserID = userID;  
  25.         m.DatePosted = DateTime.UtcNow;  
  26.   
  27.         db.Messages.Add(m);  
  28.         db.SaveChanges();  
  29.     }  
  30. }  
  31.   
  32. public int GetUserID(stringloginName)  
  33. {  
  34.     using(DemoDBEntitiesdb = newDemoDBEntities())  
  35.     {  
  36.         return db.SYSUsers.Where(o => o.LoginName.Equals(loginName))  
  37.             .SingleOrDefault().SYSUserID;  
  38.     }  
  39. }  
The GetAllMessages() method fetches all messages that was stored from the database and assigns each field values to the corresponding properties of the UserMessage model. AddMessage() method simply add new sets of data to the database. Finally, GetUserID() method gets the user id of the current logged user by passing the login name as the parameter.

STEP 5:

Add the following action methods below under Controllers folder >HomeController.cs,
  1. [Authorize]  
  2. public ActionResultIndex()  
  3. {  
  4.     UserManager UM = newUserManager();  
  5.     ViewBag.UserID = UM.GetUserID(User.Identity.Name);  
  6.     return View();  
  7. }  
  8.   
  9. [Authorize]  
  10. public ActionResultShoutBoxPartial()  
  11. {  
  12.     return PartialView();  
  13. }  
  14.   
  15. [Authorize]  
  16. public ActionResultSendMessage(intuserID, string message)   
  17. {  
  18.     UserManager UM = newUserManager();  
  19.     UM.AddMessage(userID, message);  
  20.     returnJson(new  
  21.     {  
  22.         success = true  
  23.     });  
  24. }  
  25.   
  26. [Authorize]  
  27. public ActionResultGetMessages()  
  28. {  
  29.     UserManager UM = newUserManager();  
  30.     returnJson(UM.GetAllMessages(), JsonRequestBehavior.AllowGet);  
  31. }  
In Index action method we call the GetUserID() method by passing the login name as the parameter to get the user ID of the current logged user. We then store the value in ViewBag so we can reference it in our View later on. TheSendMessage() action method simply calls the AddMessage() method to insert new records to the database. The GetMessages() method fetches all user messages from the database.

STEP 6:

Create a new partial view under Views folder > Home and name it as "ShoutBoxPartial.cshtml". And then add the following markup below:
  1. <style type="text/css">  
  2.     #divShoutBox  
  3.     {  
  4.         position: relative;  
  5.         width: 400px;  
  6.         height: 300px;  
  7.         overflow: auto  
  8.     }  
  9.      
  10.     #txtMessageText  
  11.     {  
  12.         width: 400px;  
  13.         height: 100px;  
  14.     }  
  15. </style>  
  16.   
  17. <div id="divShoutBox">  
  18.     <div id="divUserMessage"></div>  
  19. </div>  
  20.   
  21. <br/>  
  22. <text areaid="txtMessageText">  
  23.     </textarea>  
  24.     <br/>  
  25.     <input type="button" id="btnPost" value="Post" />  
  26.   
  27.     <script>  
  28.         var _isScrolling = false;  
  29.         var _lastScrollPos = 0;  
  30.         var _counter = 0;  
  31.   
  32.         $(function()  
  33.           {  
  34.   
  35.             GetMessages();  
  36.             setInterval(Fetch, 5000);  
  37.   
  38.             $("#divShoutBox").on("scroll", function()  
  39.             {  
  40.                 _isScrolling = true;  
  41.                 _lastScrollPos = this.scrollHeight;  
  42.             });  
  43.   
  44.             $("#btnPost").on("click", function()  
  45.              {  
  46.                 var msg = $("#txtMessageText");  
  47.                 var user = $("#hidUserID");  
  48.   
  49.                 if (msg.val().length > 0)  
  50.                 {  
  51.                     $.ajax({  
  52.                         type: "POST",  
  53.                         url: '@(Url.Action("SendMessage","Home"))',  
  54.                         data:  
  55.                         {  
  56.                             userID: user.val(),  
  57.                             message: msg.val()  
  58.                         },  
  59.                         success: function(d)  
  60.                         {  
  61.                             msg.val("");  
  62.                             GetMessages();  
  63.                         },  
  64.                         error: function(err) {}  
  65.                     });  
  66.                 }  
  67.             });  
  68.   
  69.         });  
  70.   
  71.   
  72.         function Fetch()  
  73.         {  
  74.             if (!_isScrolling)  
  75.             {  
  76.                 GetMessages();  
  77.                 $("#divShoutBox").scrollTop(_lastScrollPos);  
  78.             };  
  79.             _isScrolling = false;  
  80.         }  
  81.   
  82.         function GetMessages()  
  83.         {  
  84.             $.ajax  
  85.             ({  
  86.                 type: "POST",  
  87.                 url: '@(Url.Action("GetMessages","Home"))',  
  88.                 data: {},  
  89.                 success: function(d)  
  90.                 {  
  91.                     $("#divUserMessage").empty();  
  92.                     $.each(d, function(index, i)  
  93.                     {  
  94.                         GenerateHTML(i.FirstName, i.LastName, i.MessageText, FormatDateString(i.LogDate));  
  95.                     });  
  96.                 },  
  97.                 error: function(err) {}  
  98.             });  
  99.         }  
  100.   
  101.         function GenerateHTML(fName, lName, msgText, logDate)  
  102.         {  
  103.             var divMsg = $("#divUserMessage");  
  104.             divMsg.append("Posted by: " + fName + " " + lName + "<br/>");  
  105.             divMsg.append("Posted on: " + logDate + "<br/>");  
  106.             divMsg.append(msgText);  
  107.             divMsg.append("<hr/>");  
  108.         }  
  109.   
  110.         function FormatDateString(logDate)  
  111.         {  
  112.             var d = new Date(parseInt(logDate.substr(6)));  
  113.             var year = d.getFullYear();  
  114.             var month = d.getMonth() + 1;  
  115.             var day = d.getDate();  
  116.             var hour = d.getHours();  
  117.             var minutes = d.getMinutes();  
  118.             var sec = d.getSeconds();  
  119.   
  120.             return month + "/" + day + "/" + year + " " + hour + ":" + minutes + ":" + sec;  
  121.         }  
  122.     </script>  
The HTML markup above is fairly simple and there's nothing really fancy about it. It just contains some div elements, text area and button. I also applied few CSS style for the div and textbox elements. Keep in mind that the look and feel doesn't really matter for this tutorial as we are focusing mainly on the functionality itself.

Down to the JavaScript functions

There are four (4) main JavaScript functions from the markup above. The first one is the GetMessages() function. This function uses jQuery AJAX to issue an asynchronous post request to the server to get all available messages from the database. If the AJAX call is successful then we iterate to each item from the json response and call the GenerateHTML() function to build up the UI with the result set. The GenerateHTML() function uses jQuery function to build up the HTML and append the values to the existing div element. TheFormatDateString() funtion is a method that converts json date format to JavaScript date format and return our own date format to the UI for the users to see. The Fetch() function calls the GetMessages() function and handles the scroll position of the div.

This means that we auto scroll to the bottom part of the div element once there's a new message.

The $(function (){}) is the short-hand syntax for jQuery's document ready function which fires once all DOM elements are loaded in the browser. This is where we register the onscroll event of div and the onclick event of button using jQuery. In onscroll event we just set some values to some global variables for future use. In onclick event we just issued an AJAX request to server to add new data to the database. When the DOM is ready we also call the GetMessages() function to display all messages on initial load of the browser. You may also noticed there that I have used the setInterval() function to automatically pull data from the server after every five (5) seconds. So if another users from your web site sends a message then it will automatically be available for other users after 5 seconds. This is the traditional way of using AJAX to pull data from the server for a given period of time.

STEP 7:

Add the following markup below in Index.cshtml file:
  1. <input type="hidden"id="hidUserID"value="@ViewBag.UserID"/>  
  2. @Html.Action("ShoutBoxPartial""Home")  
STEP 8:

Running the code should look something like this:

output

That's it! I hope someone finds this post useful.
 
Read more articles on ASP.NET Programming: