Create an MVC wrapper for Toastr

Toastr is a great little library for creating user messages on your website. It’s written in JavaScript and is as such platform independent.

But if you’re like me (mainly working in ASP.NET MVC) then creating a server side extension to get messaging support directly in your controllers, could be something to consider.

The code shown below is kept simple so the main idea can be emphatized easier. There are several settings in Toastr that haven’t been implemented, but once the server side framework is functional you can easily add more features if wanted.

Install Toastr
Toastr is available through NuGet and can be installed using the Package Manager Console. Toastr needs jQuery to function and will install it if it’s not done.

PM> install-package toastr

When installed you also need to reference the JavaScript and CSS file that Toastr needs. By adding the following lines to App_Start/BundleConfig.cs you create bundles that can use CDN links when in production.

bundles.Add(new StyleBundle("~/content/toastr", "http://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css")
                .Include("~/Content/toastr.css"));

bundles.Add(new ScriptBundle("~/bundles/toastr", "http://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js")
                .Include("~/Scripts/toastr.js"));

(There are a few things to consider when using CDN. For example, what if the file you’re referring to will be removed from the CDN repository? There is plenty written on Internet about how to handle these cases and I won’t go into it here.)

You also need to reference your bundles in a layout file so these files will be downloaded. I prefer to do this in the Views/Shared/_Layout.cshtml file as shown here.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Styles.Render("~/Content/toastr")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    ...A big chunk of HTML code removed here...
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/toastr")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

Create the message structure
The first thing we do here is to create the structure around the messages themselves. This consists of three files: ToastType.cs, ToastMessage.cs and Toastr.cs.

ToastType is just an enum containing all the different toast types that exists. The enum values are written with with capitalization here but later on these values will be transformed into lower case values to match the toastType object values in the Toastr library.

public enum ToastType
{
  Error,
  Info,
  Success,
  Warning
}

The ToastMessage class is a simple implementation of a message to be shown. Of all options available for a toast message, only a few are implemented here. The class is marked as Serializable because we’ll be storing it in the TempData collection.

[Serializable]
public class ToastMessage
{
  public string Title { get; set; }
  public string Message { get; set; }
  public ToastType ToastType { get; set; }
  public bool IsSticky { get; set; }
}

The last class is the container object for all the messages. It has two implemented Toastr features but many more are available to be implemented.

[Serializable]
public class Toastr
{
  public bool ShowNewestOnTop { get; set; }
  public bool ShowCloseButton { get; set; }
  public List<ToastMessage> ToastMessages { get; set; }

  public ToastMessage AddToastMessage(string title, string message, ToastType toastType)
  {
    var toast = new ToastMessage()
    {
      Title = title,
      Message = message,
      ToastType = toastType
    };
    ToastMessages.Add(toast);
    return toast;
  }

  public Toastr()
  {
    ToastMessages = new List<ToastMessage>();
    ShowNewestOnTop = false;
    ShowCloseButton = false;
  }
}

Adding messages
The whole idea with our MVC wrapper is that we should be able to add messages just by writing in the following way:

public ActionResult Index()
{
   this.AddToastMessage("My first message", "A lot of important text", ToastType.Success);
   return View();
}

To do that we have two options:

  1. Implement an abstract base controller class, extending the normal Controller class, and then let all your controllers inherit from this new base controller class. You can then add an instance of the Toastr class to your new base controller class in this way. This way of implementing the solution will be covered in a separate post.
  2. Extend the controller class with an AddToastMessage method. This is a simpler solution and involves using the TempData collection for storage. The reason for using TempData and not ViewData is that user messages are saved between redirections as well. This solution is what you’ll see in this blog post.

The code in our extension method (from option 2) check if a Toastr object exists in the TempData collection. If not it will be created and then used. Finally it’ll be written back to the TempData collection.

public static class ControllerExtensions
{
  public static ToastMessage AddToastMessage(this Controller controller, string title, string message, ToastType toastType = ToastType.Info)
  {
    Toastr toastr = controller.TempData["Toastr"] as Toastr;
    toastr = toastr ?? new Toastr();

    var toastMessage = toastr.AddToastMessage(title, message, toastType);
    controller.TempData["Toastr"] = toastr;
    return toastMessage;
  }
}

Displaying messages
The messages are being displayed using JavaScript calls. The important thing here is that we make these calls after the Toastr JavaScript library has been loaded. To transform the server side message objects into JavaScript calls we can create a simple HTML helper.

This code is placed inside the App_Code folder and the filename has to end with .cshtml. In this example the file was named ToastrBuilder.cshtml.

@using MyWebApplication.Toast
@helper ShowToastMessages(Toastr toastr)
{
  if (toastr != null)
  {
    <script>
      $(document).ready(function () {
        toastr.options.closeButton = '@toastr.ShowCloseButton';
        toastr.options.newestOnTop = '@toastr.ShowNewestOnTop';

        @foreach (ToastMessage message in toastr.ToastMessages)
        {
          string toastTypeValue = message.ToastType.ToString("F").ToLower();
          @: var optionsOverride = { /* Add message specific options here */ };
          if (message.IsSticky)
          {
            @:optionsOverride.timeOut = 0; 
            @:optionsOverride.extendedTimeout = 0;
          }
          @:toastr['@toastTypeValue']('@message.Message', '@message.Title', optionsOverride);
        }
      });
    </script>
  }
}

Razor and HTML work well together but squeezing in JavaScript isn’t always easy. In the code above, lines beginning with @: are JavaScript code added within the Razor section.

The solution we’ve chosen above adds a Toastr object to TempData the first time a message is added. Since we don’t know if any Toastr object exists in the TempData collection we create a PartialView wrapper for the HTML helper. This makes it more neat in the _Layout file as well.

@using MyWebApplication.Toast
@if (TempData.ContainsKey("Toastr"))
{
  Toastr toastr = TempData["Toastr"] as Toastr;
  @ToastrBuilder.ShowToastMessages(toastr);
}

Notice the way we call the HTML helper. We combine filename (without extension) and method when calling. You also have to add the @ before calling the helper, or the data from the helper won’t be passed through.

The reason why we don’t access TempData directly from the HTML helper is because the helper is static and can’t refer to any instance objects like TempData.

The final thing we need to do is to call the PartialView from the _Layout file, after the Toastr library has been loaded, by adding the following line.

@Html.Partial("_Toastr")

Testing
To test it all we can implement two action methods in the Home controller.

public class HomeController : Controller
{
  public ActionResult Index()
  {
    this.AddToastMessage("Congratulations", "You made it all the way here!", ToastType.Success);
    return View();
  }

  public ActionResult GoSomewhereElse()
  {
    this.AddToastMessage("Redirected message", "This message has been redirected", ToastType.Info);
    return RedirectToAction("Index");
  }
}

If everything works fine you should see one user message when navigating to /Home/Index and two user messages (as seen on the picture below) when navigating to /Home/GoSomewhereElse.
Toastr example

Read more

Credits
To the creators of Toastr: