32

I would like to store different objects in the same controller content array and render each one using an appropriate view template, but ideally the same view.

I am outputting list objects using the code below. They are currently identical, but I would like to be able to use different ones.

<script type="text/x-handlebars">
  {{#each App.simpleRowController}}
    {{view App.SimpleRowView class="simple-row" contentBinding="this"}}
  {{/each}}
</script>

A cut-down version of the view is below. The other functions I haven't included could be used an any of the objects, regardless of model. So I would ideally have one view (although I have read some articles about mixins that could help if not).

<script>
  App.SimpleRowView = Em.View.extend({
    templateName: 'simple-row-preview',
  });
</script>

My first few tests into allowing different object types ended up with loads of conditions within 'simple-row-preview' - it looked terrible!

Is there any way of dynamically controlling the templateName or view used while iterating over my content array?

UPDATE

Thanks very much to the two respondents. The final code in use on the view is below. Some of my models are similar, and I liked the idea of being able to switch between template (or a sort of 'state') in my application.

<script>
  App.SimpleRowView = Em.View.extend({
    templateName: function() {
      return Em.getPath(this, 'content.template');
    }.property('content.template').cacheable(),
    _templateChanged: function() {
      this.rerender();
    }.observes('templateName'),
    // etc.
  });
</script>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
joevallender
  • 4,293
  • 3
  • 27
  • 35

2 Answers2

101

You can make templateName a property and then work out what template to use based on the content.

For example, this uses instanceof to set a template based on the type of object:

App.ItemView = Ember.View.extend({
    templateName: function() {
        if (this.get("content") instanceof App.Foo) {
            return "foo-item";
        } else {
            return "bar-item";
        }
    }.property().cacheable()
});

Here's a fiddle with a working example of the above: http://jsfiddle.net/rlivsey/QWR6V/

rlivsey
  • 2,124
  • 2
  • 18
  • 21
  • 2
    This is a very good answer, but unfortunately, this the case where the view is not added via a handlebars helper but as a class which is picked up automagically by ember. In that case, you are not able to access `content` nor the `controller` (discussed here: http://stackoverflow.com/questions/15337065/how-to-get-any-controller-instance-from-init-method-of-a-view). My solution in this case was to have a method observing the `controller.content` and setting the view accordingly in this function via `this.set('currentView', view)` – vanthome Dec 21 '13 at 16:56
16

Based on the solution of @rlivsey, I've added the functionality to change the template when a property changes, see http://jsfiddle.net/pangratz666/ux7Qa/

App.ItemView = Ember.View.extend({
    templateName: function() {
        var name = Ember.getPath(this, 'content.name');
        return (name.indexOf('foo') !== -1) ? 'foo-item' : 'bar-item';
    }.property('content.name').cacheable(),

    _templateChanged: function() {
        this.rerender();
    }.observes('templateName')
});
pangratz
  • 15,875
  • 7
  • 50
  • 75