Paul Cowan

Nomadic cattle rustler and inventor of the electric lasso

Ember.js - Rendering Dynamic Content

Warning: The examples use Ember 1.7.1

I’m not going to go into great detail in this post as I think the code examples will be out of date post ember 1.7.1.

I recently had the problem of how to make a complex table reusable accross different datasets. As each dataset would contain objects with different fields, I would not be able to use the usual handlebars syntax of binding:

1
{{property}}

My solution was to create a json hash that is similar to the one in the gist below that specified which fields I was binding to and whether or not, I was going to render a simple text field, a link or for complex scenarios a component:

hash.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
App.IndexController = Ember.ArrayController.extend({
  columns: Ember.A([
   {
      heading: "Heading",
      binding: "name",
      route: "company"
    },
    {
      heading: "Address",
      binding: "address"
    },
    {
      heading: 'full',
      component: 'full-contact',
      bindings: ['name', 'address']
    }
  ])
});
  • The address structure on line 9 is the most basic as it just specifies a property to bind to.
  • The structure on line 4 contains a route property to signify that I want a link generated.
  • The structure on line 13 contains a component property that unsurprisingly will render a component. A component can also take an array of bindings on line 15 that will have the effect of calling the component like below:
1
{{full-contact name=name address=address}}

Now in my template, I just iterate over this columns collection and either call a handlebars helper that renders the text or link (line 9) or I call out to a different helper that will render the component (line 7)

If I am rendering a simple text or link, then I call the helper that is outlined below:

getproperty.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Ember.Handlebars.registerBoundHelper('getProperty', function(context, property, options) {
  var args, defaults, id, prop;

  defaults = {
    className: "",
    context: "this",
    avatar: false
  };

  property = Ember.merge(defaults, property);
  prop = context.get(property.binding);

  if (Ember.isEmpty(prop)) {
    return "";
  }

  if (!property.hasOwnProperty("route")) {
    return new Handlebars.SafeString("<span>" + prop + "</span>");
  }

  args = Array.prototype.slice.call(arguments, 2);

  options.types = ["ID", "STRING", "ID"];
  options.contexts = [context, context, context];

  id = property.context ? "" + property.context + ".id" : "id";

  args.unshift(id);
  args.unshift(property.route);
  args.unshift(property.binding);

  return Ember.Handlebars.helpers["link-to"].apply(context, args);
});

If I am rendering a component then this helper is called:

component.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Ember.Handlebars.registerHelper('renderComponent', function(contextPath, propertyPath, options) {
  var context, helper, property;

  context = Ember.Handlebars.get(this, contextPath, options);
  property = Ember.Handlebars.get(this, propertyPath, options);

  helper = Ember.Handlebars.resolveHelper(options.data.view.container, property.component);

  options.contexts = [];
  options.types = [];

  property.bindings.forEach(function(binding) {
    options.hash[binding] = binding;
    options.hashTypes[binding] = "ID";
    options.hashContexts[binding] = context;
  });

  return helper.call(context, options);
});

Here is a working jsbin.

That is all I have to say on the matter but I would love to hear an alternative or better approach to the above.

Comments