Custom JavaScript KungFu and management for Microsoft MVC.NET

Rails community got a lot things right for web application. One of them is the way they manage JavaScript files. I’m working on a web project where I use a lot of JavaScript and MVC.NET as Web Framework. Team is quite big and JavaScript is getting out of control. Files are everywhere with a lot of code. And then when it goes into production it is the best to have it as one compressed and minified file. So, here what we did.

Where am I, ups, production, I better behave

The easiest possible way of telling the app where it is running will be in Web.config. There is already a bunch of custom setting in there, so why not just have one more. When the time comes based on the Environment information we can decide if we will still spit out single un-minified files for a development environment or serve one minified file for lightning fast production performance.

Help me, help me, Mr Helper

So, the idea with the helper is: we use it to include every single file that we need on our page. When it knows that we are on Production it will replace imports with one, plus potentially minify it and compress. So I created a class called JSHelper.

JSHelper will contain two methods, one will kind of register the file for inclusion, and the other will do the actual job. So, the C# code goes somewhat like this:

public class JSHelper
{
     private readonly IList<string> listOfFilesToInclude = new List<string>();
     public const string KEY = "JSHelper_Key";

     public void IncludeJs(string fileName)
     {
          if (!listOfFilesToInclude.Contains(fileName))
          {
                listOfFilesToInclude.Add(fileName);
          }
     }

     public string IncludeAllJs()
     {
           var scriptRoot = VirtualPathUtility.ToAbsolute("~/Scripts");
           var scriptFormat = "<script src=\"{0}/{1}\" type=\"text/javascript\"></script>\r\n";
           var importedJavaScripts = "";
           foreach (var fileName in listOfFilesToInclude)
           {
                importedJavaScripts += string.Format(scriptFormat, scriptRoot, fileName);
           }
           return importedJavaScripts;
     }
}

So, as you can see it will also eliminate potential errors of including the same JS file twice.

Now to solve next problem. The JSHelper needs to be a single instance per Web Request. It needs to be as it holds the state of all the files to be imported. Having a static means that it is shared across all web requests, not good. It has to be an instance for a every server thread. Controllers are save place for our helper to be, plus they can put stuff to a ViewData. As you might or not be aware this is accessible in aspx View template.

Take over Control

In MVC.NET there is base class for all controllers that every controller inherits. It is called Controller. Because we got some more stuff to setup in all our controller we created our own that extends base framework controller and every controller in our application extends it. This is a great place to put our JSHelper in. Code goes something like that:

public abstract class ApplicationController : Controller
{

     protected override void OnActionExecuting(ActionExecutingContext filterContext)
     {
          ViewData[JSHelper.KEY] = new JSHelper();
          base.OnActionExecuting(filterContext);
     }

}

This code will create new instance of JSHelper and put it into your ViewData for usage. Because we also extend MVC.NET base class we can have JSHelper accessible in every View we got in the application.

My VIEW of the world

So the code goes something like this:

public class BaseViewPage<TModel> : ViewPage<TModel> where TModel : class
{
    public JSHelper JSHelper
    {
        get { return (JSHelper) ViewData[JSHelper.KEY]; }
    }

}

Something similar I done for the Master Page. This way we could combine all JavaScripts into one place.

So, the example of a View with JSHelper looks like this.

Master page

<head>
<% JSHelper.IncludeJs("jquery.js");%>
<% JSHelper.IncludeJs("other-awesome-library.js"); %>

<asp:ContentPlaceHolder ID="headContent" runat="server" />

<%= JSHelper.IncludeAllJs() %>

</head>

<body>

View

<asp:Content ContentPlaceHolderID=”headContent” runat=”server”>

<% JSHelper.IncludeJs("blabla.js");%>
<% JSHelper.IncludeJs("hm-hm.js");%>

</asp:Content>

Final pieces

Now we can put into use our Web.config file and change IncludeAllJs method to serve files based on environment we are in. Not posting implementation as it could be achieved in many ways.

So, when we are serving JS on production environment we can serve one single file prepared before as a deployment task or we can dynamically prepare it by pointing web browser to another controller that will combine all the files and minified it on a flight.

You can look at some JavaScript code minification in here: http://www.crockford.com/javascript/jsmin.cs

Let me know if you find this ideas useful.

Cheerios, Greg

One thought on “Custom JavaScript KungFu and management for Microsoft MVC.NET

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s