How to render a view when its class varies based on model attributes


#1

I desperately need help with an ambitious app I am working on…

The main data model is a dashboard (a user could have multiple dashboards), each dashboard has a number of rows which should be represented by a row view to handle some user input…in addition, each row has number of widgets, since widgets vary wildly when interacting with them they all share the same data model. I made the dashboard to be a CollectionView where it contains row views. so far so good, here comes the tricky part, if I use a CollectionView I will have to override createChildView and try to read the “type” attribute of the model, but it is always null since it is loaded a synchronously via ember-data restful adapter. thus I can’t return the correct class for the widget. I am really stuck here and I really don’t want to switch away from Ember. Feel free to question any of the implied decisions that I made.

Please help me on how to solve this and eventually have widget views nested correctly in row views, which in turn nest correctly the dashboard view.


#2

If you’re still looking for a solution, I made you a jsFiddle showing how to choose a view class at runtime based on the type attribute of a model. This should do the right thing with asynchronously loaded data, too.

Here are the key bits:

<script type="text/x-handlebars" data-template-name="widget">
    <h2>{{name}}</h2>
    <div class="content">
        <!-- This is the magic bit. -->
        {{view view.subViewType}}
    </div>
</script>

…and:

App.WidgetView = Ember.View.extend({
    templateName: "widget",
    classNames: ["widget"],
    
    // Choose what kind of view we want to use to render this widget.
    // This gets used by our template.
    subViewType: function () {
        console.log("context:", this.get("context"));
        switch (this.get("context.type")) {
            case "weather": return App.WeatherView;
            case "scores": return App.ScoresView;
            default: return App.LoadingView;
        }
    }.property("context.type"),
    
    // We need to trigger this rerender manually when subViewType
    // changes, because Handlebars {{view ...}} isn't smart enough.
    subViewTypeDidChange: function () {
        this.rerender();
    }.observes("subViewType")
});

#3

Exactly what I wanted … Thank you for taking the time and answering my question.


#4

Another possibility would be to just switch the templateName and trigger a rerender() when the templateName property has changed.