Using Toastr With SignalR

John Holland .NET, ASP.NET, Development Technologies, Tutorial 1 Comment

Attention: The following article was published over 7 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

Introduction

I was recently introduced to the JavaScript library Toastr. It provides an easy-to-use, non-blocking, and consistent way to provide messaging to the user of an application using JavaScript and CSS.

When you submit data to the server within a Single-Page Application (SPA), you are not performing the usual form post of data that would generate a new page load. Rather, you send the data using AJAX and are able to parse the response returned by the API using JavaScript and can act accordingly. In the past, this would most likely come in the form of an alert or populating a div with the appropriate message. This approach did what I needed it to do, essentially notifying the user of success or failure and allowing them to move on with their work.

Enter Toastr. Now I can have a non-blocking, consistent way in which to display these messages to the user, that are styled respective to the type of message it is. Toastr provides 4 different types of messages: Success, Error, Warning and Info. This makes things consistent and allows me to not have to think about this aspect of the development.

This is where the story gets interesting. So with this easy to use, non-blocking, consistent way of displaying messages, you can pair it up with SignalR and provide that same messaging from the server-side. (Not familiar with SignalR? View these blog posts.)

In this blog, we will demonstrate the use of the Toastr messaging library and how, when you couple it with SignalR, it can provide you with an easy-to-use, consistent messaging alternative.

This pairing was an important revelation for me. I do realize, now, that it makes perfect sense to integrate them. Now, if I need to make a post to the server that requires a long-running process to happen; that would normally in the past have prevented the user from proceeding. But now, I can return a message to the user that the process will let them know when it’s done, or just use a void and send back nothing, so they can get on with what they are doing, and then once the process is done, I can send them a message of completion.

New Project Setup

To demonstrate the use of SignalR and Toastr, let’s go through the following steps to get a new project setup and everything installed.

1. Create a new project.

In Visual Studio, click “New Project / ASP.NET MVC 4 Web Application / Internet Application.” This should install an MVC default site that works. For the sake of the rest of this code, my application is named “MvcApplication2,” so you will need to adapt the code accordingly.

Run it to make sure the site comes up in the browser with no errors. Click the “About” and the “Contact” and make sure it works.

2. Now install SignalR into your solution.

  • Click “Tools / NuGet Package Manager / Manage NuGet Packages for Solution”
  • Search for “signalr”
  • Choose “Microsoft ASP.NET SignalR”, click “Install”
  • Close NuGet

3. Create App_Start/Startup.cs.

Do so using this:

	using Microsoft.Owin;
	using Owin;
	using MvcApplication2;
	using Microsoft.AspNet.SignalR;

	// ** Use correct application name
	[assembly: OwinStartup(typeof(MvcApplication2.App_Start.Startup))]
	namespace MvcApplication2.App_Start
	{
	    public class Startup
	    {
	        public void Configuration(IAppBuilder app)
	        {
	            app.MapSignalR();
	        }
	    }
	}

4. Create your SignalR Hub.

Create Hubs directory, and then create MessageHub.cs in it, using this:

	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Threading.Tasks;
	using System.Web;
	using Microsoft.AspNet.SignalR;

	namespace MvcApplication2.Hubs
	{
	    public class MessageHub : Hub
	    {
	        private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
	        private static List<String> _connectedClients = new List<String>();

	        public override Task OnConnected()
	        {
	            var tmp = Context.User.Identity.Name;
	            //HttpContext.Current.Session.Add("connectionId", Context.ConnectionId);
	            _connectedClients.Add(Context.ConnectionId);
	            return base.OnConnected();
	        }

	        public void ConnectToMessageHub(string username)
	        {
	            // MAP LOGICAL USER "LOCATIONID" TO THE "CONNECTIONID" USING GROUPS.
	            // THIS ALLOWS US TO SEND TO THE "GROUP" OUTSIDE OF THE HUB, WHERE THERE
	            // IS NO CONCEPT OF THE "CONNECTIONID"
	            Groups.Add(Context.ConnectionId, username);
	            System.Diagnostics.Debug.WriteLine("HUB: CONNECTFORCHECKINS: " + username);
	        }
	
	        public static void SendToast(string groupId, string toastType, string content, string title)
	        {
	            hubContext.Clients.Group(groupId).sendToast(toastType, content,title);
	        }
	    }
	}

5. Newtonsoft.Json

In the Web.config in the “runtime / assemblyBinding” section, confirm Newtonsoft.Json has been added. If it hasn’t, then add this:

	<dependentAssembly>
		<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
		<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
	</dependentAssembly>

Run the site and make sure it runs without errors.

6. Now install Toastr into your solution.

  • Click “Tools / NuGet Package Manager / Manage NuGet Packages for Solution”
  • Search for “toastr”
  • Chose “toastr”, click “Install”
  • Close NuGet

After it is installed you should see these new files in your project:

	Content/toastr.css
	Content/toastr.less
	Content/toastr.min.css
	Content/toastr.scss
	Scripts/toastr.js
	Scripts/toastr.min.js

7. Formatting

I wanted to put my messages in the center of the screen so I added a new class to the Content/toastr.css file.

This would be the only thing I see missing from Toastr currently. Here is that css class:

.toast-center {
    top: 50%;
    left: 50%;
    right: 0;
    width: 300px;
    margin-top: -50px;
    margin-left: -150px;
}

8. Now, we will add the inclusion of the JS and CSS files along with some JavaScript methods that we will be calling.

In my connecting to the SignalR messageHub, I am passing a static string as my username. This would have to be something more robust in a real-world application, but for testing purposes (and because I don’t have an authentication layer locally), I hardcoded the username.

Open “Views / Shared / _Layout.cshtml.” Right after @Scripts.Render("~/bundles/jquery"), add this:

	<!-- ** MAKE SURE THIS VERSION IS CORRECT WITH WHAT WAS INSTALLED IN YOUR SOLUTION -->
	@Scripts.Render("~/Scripts/jquery.signalR-2.2.0.min.js")

        <!-- REFERENCE THE AUTO-GENERATED SIGNALR HUB SCRIPT THAT RESIDES ON THE API -->
        @Scripts.Render("~/signalr/hubs")
        
        @Styles.Render("~/Content/toastr.css")
        @Scripts.Render("~/Scripts/toastr.js")

Then after @RenderSection("scripts", required: false), add this:

	< scri pt type="text/javascript">
		// METHODS CALLED FROM LINKS
		$('#successLink').click(function () {
			console.log('Successlink clicked');
			$.ajax({
				url: '/Home/ToastrMessage?toastType=success',
				success: function (data) { console.dir(data); }
			});
		});
		$('#successLinkClientside').click(function () {
			console.log('SuccesslinkClientside clicked');
			toastr['success']('Here is my client-side Success message', 'SUCCESS title');
		});
		$('#errorLink').click(function () {
			console.log('Errorlink clicked');
			$.ajax({
				url: '/Home/ToastrMessage?toastType=error',
				success: function (data) { console.dir(data); }
			});
		});
		$('#infoLink').click(function () {
			console.log('Infolink clicked');
			$.ajax({
				url: '/Home/ToastrMessage?toastType=info',
				success: function (data) { console.dir(data); }
			});
		});
		$('#warningLink').click(function () {
			console.log('Warninglink clicked');
			$.ajax({
				url: '/Home/ToastrMessage?toastType=warning',
				success: function (data) { console.dir(data); }
			});
		});

		// TOASTR OPTIONS
		toastr.options = {
			'closeButton': false,
			'debug': false,
			'newestOnTop': false,
			'progressBar': false,
			'positionClass': 'toast-center',
			'preventDuplicates': true,
			'onclick': null,
			'showDuration': '5000',
			'hideDuration': '1000',
			'timeOut': '5000',
			'extendedTimeOut': '1000',
			'showEasing': 'swing',
			'hideEasing': 'linear',
			'showMethod': 'fadeIn',
			'hideMethod': 'fadeOut'
		};

		// SIGNALR CONNECTION AND METHOD
		$(function () {
			$.connection.hub.start()
				.done(function () {
					console.log('SignalR connected, connection id = ' + $.connection.hub.id);
					console.log('Username = ' + '001');
					$.connection.messageHub.server.connectToMessageHub('001');
				})
				.fail(function (data) {
					console.log('SignalR failed to connect: ' + data);
				});

			// METHOD CALLED FROM SERVER-SIDE
			$.connection.messageHub.client.sendToast = function (type, content, title) {
				console.log("sendToast called");
				toastr[type](content, title);
			};
		});
	< / script >

9. Now, I create my method in Controllers/HomeController.cs, so the above URLs will work.

I put in an artificial delay for each of the different message types. This is to simulate work being done.

public void ToastrMessage(string toastType = "success")
{
	// DEFAULT DELAY
	var sleepMilliseconds = 1000;
	switch (toastType)
	{
		case "success": sleepMilliseconds = 3000; break;
		case "error": sleepMilliseconds = 5000; break;
		case "info": sleepMilliseconds = 7000; break;
		case "warning": sleepMilliseconds = 1000; break;
	}

	// SIMULATE WORK
	var stopwatch = new System.Diagnostics.Stopwatch();
	stopwatch.Start();
	System.Threading.Thread.Sleep(sleepMilliseconds);
	var elapsedTime = stopwatch.ElapsedMilliseconds;

	// USE CORRECT APPLICATION NAME OR INCLUDE IN "using ..." STATEMENTS
	MvcApplication2.Hubs.MessageHub.SendToast("001", toastType, toastType.ToUpper() + " Message goes here. Took " + elapsedTime + " milliseconds", toastType.ToUpper() + " title");
}

10. Finally, I add the anchors into the Views/Home/Index.cshtml to assign the click events.

Nothing fancy here, but rather just showing each of the different message types.




<div>
    <a id='successLink'>Success link</a>
    <a id='successLinkClientside'>Success link (client-side)</a>
    
<a id='errorLink'>Error link</a>
    
<a id='infoLink'>Info link</a>
    
<a id='warningLink'>Warning link</a>
</div>




Now to demonstrate, start up your app in the browser. You should be able to click each of the links and have its corresponding message type display. After you click any of the server-side links, click off to another page, or several depending on the delay. When the delay is over, the Toastr message should appear.

Another Use Case

Another helpful use-case might be if you have a workflow application, and an item’s status is changed. Normally you may send an email to the person that submitted the item to notify them of the change in status.

In addition, you could send an immediate notification using Toastr so that if they are logged into the application, no matter where they are located, they would see the message and be able to act immediately.

Final Thoughts

Thanks for reading! In this blog, I demonstrated the use of the Toastr messaging library. Following the steps, we showed how Toastr provides an easy-to-use consistent messaging alternative when coupled with SignalR.

This is but one example; the possibilities are almost endless to what you can do with Toastr and SignalR. What other scenarios do you feel would be a good match for SignalR & Toastr?

0 0 votes
Article Rating
Subscribe
Notify of
guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments