Fat-Free Templating with Barebones Javascript

Today I’m going to demonstrate a straight-forward and very effective technique for markup templating.  I’ve borrowed the supplant prototype function from Douglas Crockford’s – And Then There Was Javascript presentation, in order to show you how to inject values from custom data structures directly into into strings.  We’ll be doing a minimal implementation here in order to show the great outcomes you can get without requiring buy in to one of the many over engineered frameworks that do this. 

First we extend the prototype of the built in String type.

String.prototype.supplant = function(o) {

    return this.replace(/{([^{}]*)}/g,

        function(a, b) {

            var r = o[b];

            return typeof r === ‘string’ || typeof r === ‘number’ ? r : a;

        }

    );

};

With this minimalist formatter in place we are able to do string manipulation like this:

            var result = "{a}-{b}-{c}".supplant({"a": "Foo", "b": "Bar", "c": "Baz" });

The resulting string would be: "Foo-Bar-Baz".  As you can see, we’ve essentially got a string formatting extension to all objects of type String here.  Where this becomes immediately useful to us in our web pages, is for injecting objects directly into markup strings, which can then be programmatically added to the DOM.  This technique is shown below in only  4 lines (statements) of Javascript.

      <div id="container" />

 

      <script type="text/javascript">

 

        var template = ‘<table border="{border}">’ +

                        ‘<tr><th>Last</th><td>{last}</td></tr>’ +

                        ‘<tr><th>First</th><td>{first}</td></tr>’ +

                       ‘</table>’;

 

        var data = {

            "first": "Carl",

            "last": "Hollywood",

            "border": 2

        };

 

        var container = document.getElementById("container");

        container.innerHTML = template.supplant(data);

   

      </script>

 

As you can see, we start with a template String and a JSON data Object.  Then we get our target DOM node and simply set its .innerHTML property to the output of the template running its own .supplant() function against the data object.  Very sparse, no heavy framework..  Its just the bare bones Javascript and a helper function.  Here’s an example of using this technique to set the .src property of an <img> DOM node.

Assuming I have an image file that I want to be targetable based on some script logic, it looks like this.

The code that I need to target this file at runtime looks like this:

            <img id="logo" src="" />

      <script type="text/javascript">

 

          var param = { domain: ‘memecannon.com’, media: ‘/Content/ninja’ };

          var url = "{media}.jpg".supplant(param);

 

          var logo = document.getElementById("logo");

          logo.setAttribute("src", url);

   

    </script>

 

The output looks like this:

Using this approach, its quite easy to do targeting of custom skin artifacts and layout structures.  Other uses of this approach are to programmatically inject variables into CSS strings and runtime script includes.  As you can see, the basics for screen templating are exposed near the surface of DOM scripting with Javascript.  That said, I do typically use JQuery plugins for this type of screen composition.  I’ve surveyed most of the JQuery plugins in this space.  I recommend JSRepeater for ease of use and support for automatic iterating over collections and dealing with heirarchal data structures.  For an extremely unobtrusive templating solution, you can’t beat NoTemplate.  It provides a level of externalized purity, in that you don’t need to hack replacement ${targets} into your markup at all; rather, it uses selectors to reach down into your markup for mapping data to screen targets. 

Hopefully, in this article I’ve informed, if not convinced you that you have the power to blend your data and markup in whatever ways you need purely with Javascript and with a minimum of effort.  In a successive post,  I’ll show you how to interact this approach with Ajax Services to bring your screens to life.

Enjoy..

Implementing method_missing with C# dynamic – Part 2

In my previous post, Implementing method_missing with C# dynamic – Part 1, I demonstrated a simple approach to plugging a method_missing call routing seam into a DynamicObject.  Here I’ll take it a step further to implement a generic method_missing function capable of passing any call to a forwarding context object.  Note that I do not have to define the method_missing in the calling code; its now automatically setup for me in DynamicObject itself.

             

[TestMethod]

public void Can_Forward_Through_Default_Missing_Method()

{

    dynamic dispatcher1 = new ExpandableDispatcher();

    dynamic dispatcher2 = new ExpandableDispatcher();

    dynamic dispatcher3 = new ExpandableDispatcher();

 

    //configure forwarding

    dispatcher1.ForwardContext = dispatcher2;

    dispatcher2.ForwardContext = dispatcher3;

 

    //set responder on dispatcher3

    dispatcher3.Methods["RunMeta"] = new Func<string, string>(param =>

    {

        return "Meta said " + param;

    });

 

    //try to execute the responder from dispatcher1

    var response = dispatcher1.RunMeta("I am a probe..");

 

    Assert.IsTrue(response == "Meta said I am a probe..");

}

 

The implementation of ExpandableDispatcher contains a DynamicObject reference called ForwardContext.  This handle is for forwarding messages that cannot be responded to by the this in the current execution context.  Note that in the ctor, the dispatcher sets up its own, method_missing.  

 

public class ExpandableDispatcher : DynamicObject

{

    DynamicObject forwardContext;

 

    public DynamicObject ForwardContext

    {

        get { return forwardContext; }

        set { forwardContext = value; }

    }

 

    IDictionary<string, object> _methods = new Dictionary<string, object>();

 

    public IDictionary<string, object> Methods

    {

        get { return _methods; }

        set { _methods = value; }

    } 

   

    public ExpandableDispatcher()

    {

 

        //setup default method_missing

        this._methods["method_missing"] = new Func<DynamicObject, InvokeMemberBinder, object[], object>((contextObject, binder, args) =>

        {

 

            dynamic context = contextObject;

 

            var method = context.Methods.ContainsKey(binder.Name) ? context.Methods[binder.Name] : null;

            var method_missing = context.Methods.ContainsKey("method_missing") ? context.Methods["method_missing"] : null;

 

            if (method != null)

            {

                if (method.ToString().StartsWith("System.Action"))

                {

                    RunAction(method, args);

                }

                else

                {

                    return RunFunc(method, args);

                }

            }

            else if (method_missing != null)

            {

                return method_missing(context.ForwardContext, binder, args);

            }

 

            return null;

        });

    }

 

    private object RunFunc(dynamic method, object[] args)

    {

        switch (args.Length)

        {

            case 0:

                return method();

            case 1:

                return method(args[0]);

            case 2:

                return method(args[0], args[1]);

            case 3:

                return method(args[0], args[1], args[2]);

            case 4:

                return method(args[0], args[1], args[2], args[3]);

            case 5:

                return method(args[0], args[1], args[2], args[3], args[4]);

        }

    }

 

    private void RunAction(dynamic method, object[] args)

    {

        switch (args.Length)

        {

            case 0:

                method();

                break;

            case 1:

                method(args[0]);

                break;

            case 2:

                method(args[0], args[1]);

                break;

            case 3:

                method(args[0], args[1], args[2]);

                break;

            case 4:

                method(args[0], args[1], args[2], args[3]);

                break;

            case 5:

                method(args[0], args[1], args[2], args[3], args[4]);

                break;

        }

    }

 

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)

    {

        if (_methods.ContainsKey(binder.Name) && _methods[binder.Name] is Delegate)

        {

            result = (_methods[binder.Name] as Delegate).DynamicInvoke(args);

            return true;

        }

        else if (_methods.ContainsKey("method_missing") && _methods["method_missing"] is Delegate)

        {

            var method_missing = _methods["method_missing"] as Delegate;

            var ctxParam = method_missing.Method.GetParameters().Where(p => p.Position == 0 &&

                                                                        p.ParameterType == typeof(DynamicObject)).FirstOrDefault();

            if (ctxParam != null && forwardContext != null)

            {

                dynamic context = forwardContext;

                result = method_missing.DynamicInvoke(context, binder, args);

                return true;

            }

            else

            {

                result = method_missing.DynamicInvoke(binder, args);

                return true;

            }

        }

        else

        {

            return base.TryInvokeMember(binder, args, out result);

        }

    }

}

 

For further reading on the topics I discussed and demonstrated in this series, this MSDN article shows a clever approach to creating MethodBags by passing lambda Expressions to a DynamicObject, which are then compiled into Delegates and assigned to to the dynamic itself.  In successive posts, I’ll be exploring the new extensions to the System.Linq.Expressions.Expression API in .NET 4.0.

Enjoy..

Implementing method_missing with C# dynamic – Part 1

One of the neat things about Ruby is its method_missing fallback capability.  Using the C# 4.0 DynamicObject, we’re also able to control dispatch at runtime.  I wanted to see how the method_missing idiom might work with a C# dynamic dispatcher, so I wrote up a small sample.

[TestMethod]

public void Can_Control_Dynamic_Dispatch()

{

    dynamic dispatcher1 = new ExpandableDispatcher();

 

    dynamic dispatcher2 = new ExpandableDispatcher();

 

    dispatcher1.Methods["method_missing"] = new Func<string, object>(param =>

    {

        return dispatcher2.RunMeta(param);

    });

 

    dispatcher2.Methods["RunMeta"] = new Func<string, string>(param =>

    {

        return "Meta said " + param;

    });

 

    var response = dispatcher1.RunMeta("I am a probe..");

 

    Assert.IsTrue(response == "Meta said I am a probe..");

}

 

Note that an attempt to execute RunMeta is made on dispatcher1, but RunMeta is not defined on dispatcher1; its on dispatcher2.  The call gets routed by method_missing to dispatcher2 for execution.  In this simple case, the call is passed from a Delegate in dispatcher1 to one in dispatcher2 via a closure reference set in the calling code.    A more elaborate message passing implementation, however, will allow method_missing to forward the call to a ServiceLocator or back up the call stack until it finds a method with a matching name and signature. 

 

Here’s the simplest implementation of ExpandableDispatcher.

public class ExpandableDispatcher : DynamicObject

{

 

       IDictionary<string, object> _methods = new Dictionary<string, object>();

public IDictionary <string, object> Methods

{

    get { return _methods; }

    set { _methods = value; }

}

 

 

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)

        {

            if (_methods.ContainsKey(binder.Name) && (_methods[binder.Name] is Delegate)

            {

                result = (_methods[binder.Name] as Delegate).DynamicInvoke(args);

                return true;

            }

            else if (_methods.ContainsKey("method_missing") && _methods["method_missing"] is Delegate)

            {

                result = (_methods["method_missing"] as Delegate).DynamicInvoke(args);

                return true;

            }

            else

            {

                return base.TryInvokeMember(binder, args, out result);

            }

        }

}

 

As you can see, dynamic dispatch in C# 4.0 gives us the power to define method calling routes with an emphasis on runtime behavioral composition.  This enables a new family patterns in C# that leverage dynamic message routing using runtime assignment of Delegates to DynamicObject facades.  For more reading on this topic, see the follow up post, Implementing method_missing with C# dynamic – Part 2.

Enjoy..