Director.js: Dynamic Template Decorating From Declarative Attributes

Lately I’ve been doing lots of liberal caching in Rails with cache_digests for the super fast rendering it grants you. The more logic-less the templates are, the faster they render initially and the harder and more globally they can be cached.

For example, if there are no conditional checks involving current_user in a template, it can be cached to an entire account or site-wide rather than on a per user basis.

This caching pattern often leaves the need to spruce up templates with JavaScript after the page is rendered though. Consider the following template:

1
2
3
4
5
6
7
8
9
<% cache current_account do %>
  <label>Assigned to:</label>
  <select name="user_id">
    <option value="1">John Doe</option>
    <option value="2">Jane Doe</option>
    ...
  </select>
  </div>
<% end %>

Say you wanted to initialize the select by selecting the option of the currently signed in user. Normally I would add a small JavaScript function to fire on $(document).ready or on the page:change event. But flipping between JavaScript and templates on an individual basis felt tedious and inelegant, so I came up with the following tiny plugin:

director.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
direct = (opts) ->
  return unless $.isPlainObject opts
  _.reduce _.pairs(opts), (memo, opt) ->
    fn = director.directives[opt[0]] or $.fn[opt[0]]
    return memo unless _.isFunction fn
    fn.call(memo, opt[1])
  , $(@)

@director =
  direct: (el = "html") ->
    $(el).find("[data-direct]").each ->
      direct.call @, $(@).data('direct')
  directives: {}

What is going on here? Firstly, it assumes you are using jQuery (or Zepto with the data module) bound to $ and Underscore or Lo-Dash bound to _. Why re-invent the wheel, right?

If you call director.direct() without a variable, it will start at the top of the DOM but you can also call director.direct('#foo') to scope it to an element with the ID of ‘foo’. Director will then look within the scope for instances of data-direct and employ the handy ability of jQuery’s .data() method to turn an attribute value from JSON into an object.

_.reduce iterates through the pairs of the attribute-turned-object and calls they key as a function on $(@), passing in the value as the parameter. The key could then be any jQuery function. The memoized result is returned to the next key/pair so you can chain the statement together.

Given the original example above, you can then add something like this higher up the DOM:

1
<div id="assign" data-direct='{"find":"select","val":"<%= current_user.id %>"}'>

When director.direct() is called, without writing one-line of JavaScript this would automagically get turned into:

1
$("#assign").find("select").val("2")

I also created an empty plain object of director.directives for adding any special functionality that simple jQuery commands can’t provide. Just make sure to return this so you can chain your statements together.

For more examples and further development, star director on Github.

Comments