@Blog.Author(Nandip Makwana) .LearningExperience(ASP.NET, ASP.NET MVC, IIS, jQuery & Technology Surrounding it...)

November 12, 2012 comment , , , , ,

ASP.NET MVC Bundling: Auto sync JavaScript model with MVC model

In yesterdays post ASP.NET 4.5 & MVC 4 Bundling: Next Approach, we have seen how we can pass additional parameters to bundle transform so we can utilize it while generating bundle response. I suggest you to read that post first before starting with this post.

What was the original idea?

Recently in one application, I have to create one JavaScript object which represents C# enum in client side. Later on C# enum keep extending and at the same time I used to add entry in JavaScript object too. Everything was fine but sometime me or team member forget to sync JavaScript object with C# enum when adding or removing existing entry in C# enum and JavaScript starts breaking and further more each time syncing JavaScript object with C# enum was also tedious task specially when team member is new and do not know whole application architecture. So I though let me create one HTTP handler which generates dynamic JavaScript which represent C# enum.

Later when I started implementing HTTP handler, I realize that there are many other JavaScript that can be generated from other C# type and could save developer time & headache especially from task such as syncing JavaScript object/class with C# type. Let me elaborate whole scenario. The application was in MVC and for user friendliness & performance point of view we all are using lots of AJAX & JSON. For e.g.

  • MVC application return list of item in JSON format via JSON result, and we bind it with UI
  • Client post JSON object back to server and desterilize it back in MVC model or model binder take care of it
  • Some time we are using JavaScript MVVM library such as knockout.js or sometime we are using library like backbone.js
  • Microsoft also introduced Single Page Application (SPA) with set of JavaScript library
  • And many other approaches with AJAX, JSON

In short when you are creating rich application, model in client side (JavaScript) is also as important as server side MVC model, entity model or whatever approach you are using. And most of time JavaScript model is same as server side model. So now let back to main point… when I implemented HTTP handler, I thought let me generate JavaScript class for server side model too. So whenever any new property added or removed at that time it also syncs JavaScript class with it.

Later in this post, we will also see how we can use this auto generated JavaScript model with JavaScript library like knockout.js and backbone.js

Why & How I ended up with bundling in ASP.NET MVC application

I could implement it as HTTP handler and almost implemented but later I thought let me see if I could create custom bundle and transform for the same. There are few advantages of using bundling over HTTP handler while response are same application wide irrespective of user. It is

  • It stores generated response in server cache hence it is faster than processing each and every request to HTTP handler (read my full post on same here)
  • We can configure bundle url something pretty like ~/bundle/jsmodel over ~/jsmodel.ashx (yeah we can configure route for the same but still…)
  • We can clearly separate transformation logic in separate class/library etc.

Following is the full source code for the same staring with Bundle which accepts .NET type as an extra parameter.

public class JSModelBundle : Bundle
{
    private List<Type> _modelList = new List&lt;Type&gt;();
 
    public JSModelBundle(string virtualPath)
        : base(virtualPath, new JSModelTransform())
    {
    }
 
    public List<Type> ModelList
    {
        get { return _modelList; }
        set { _modelList = value; }
    }
}

Transform type which transforms .NET type in JavaScript object/class.

public class JSModelTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        string strResponse = string.Empty;
        response.ContentType = "text/javascript";
 
        JSModelBundle bundle = (context.BundleCollection
            .Where(b => b.Path == context.BundleVirtualPath)
            .FirstOrDefault()) as JSModelBundle;
 
        foreach (Type type in bundle.ModelList)
        {
            if (type.IsClass)
            {
                strResponse += GetFunctionBodyForClass(type);
            }
            else if (type.IsEnum)
            {
                strResponse += GetObjectBodyForEnum(type);
            }
        }
 
        response.Content = strResponse;
    }
 
    string GetFunctionBodyForClass(Type type)
    {
        StringBuilder sbFunctionBody = new StringBuilder();
        sbFunctionBody.AppendLine("function " + type.Name + "() {");
 
        foreach (PropertyInfo p in type.GetProperties())
        {
            sbFunctionBody.AppendLine("this." + p.Name + " = '';");
        }
 
        sbFunctionBody.Append("}");
        sbFunctionBody.Append("\n\n");
        return sbFunctionBody.ToString();
    }
 
    string GetObjectBodyForEnum(Type type)
    {
        StringBuilder sbFunctionBody = new StringBuilder();
        sbFunctionBody.AppendLine(type.Name + " = {");
 
        int enumLength = type.GetEnumValues().Length;
        int index = 1;
 
        foreach (var v in type.GetEnumValues())
        {
            string strEnumField = v.ToString() + " : " + (int)v;
            if (index &lt; enumLength)
                strEnumField += ",";
            sbFunctionBody.AppendLine(strEnumField);
            index++;
        }
 
        sbFunctionBody.Append("}");
        sbFunctionBody.Append("\n\n");
        return sbFunctionBody.ToString();
    }
}

Sample .NET types

public class Product
{
    public int ProductId { get; set; }
    public string ProductCode { get; set; }
    public string ProductName { get; set; }
}
 
public enum Event
{
    Add,
    Update,
    Delete,
}

and code snippet for how to include it in bundle

JSModelBundle bundle = new JSModelBundle("~/bundles/JsModel");
bundle.ModelList.Add(typeof(Product));
bundle.ModelList.Add(typeof(Event));
bundles.Add(bundle);

Now we can use generated JavaScript model with backbone.js as displayed below.

var product = new Backbone.Model(new Product());

Following is the code snippet of using it with knockout.js

var product = new Product();
ko.applyBindings(product);

Hope this would be helpful!

Check out this index post on ASP.NET Web Optimization Framework (a.k.a Bundling & Minification) to read other post on same.

You can follow me on twitter for latest link and update on ASP.NET & MVC.

comments powered by Disqus

Featured Content

Resources & Tools

About Nandip Makwana

Nandip Makwana is passionate about digital world and web. He completed his Masters in Computer Application in June 2011. Currently he is working as a Software Engineer. He has shown great promise and command over ASP.NET and technologies surrounding it during his academic years and professorial life...continue reading