3
votes

I am building an app using backbone.js and find myself with a view with a lot of conditional logic in the templating of the Model's View. The type property of the model is used to determine which html to render. I would like to avoid this logic if possible, because it is hard to read. There are a couple of ways that I think I could deal with this (Note that the actual templates I've got here are very much simplified):

1. Conditional Logic in Collection View rather than in Model View - Multiple Subviews

I could put the conditional logic that acts on each model's type into the Collection View:

var CollectionView = Backbone.View.extend({
    ....
    render: function() {
        this.collection.each(function(thing) {
            if(thing.get("type") === "wotsit") {
                this.$el.append(new WotsitView({ model: thing });
            } else if(thing.get("type") === "oojamaflip") {
                this.$el.append(new OojamaflipView({ model: thing });
            }
        }, this);
    },
    ....
}

Pros This way I could have each subview with a template method that had no logic in it, but rather just builds html.

var WotsitView = new Backbone.View.extend({
    ....
    template: _.template('<h2>{{ title }}</h2>');
});
var OojamaflipView = new Backbone.View.extend({
    ....
    template: _.template('<h3>{{ title }}</h3>');
});

Cons The thing is, the things in the collection are all very similar. The events for each thing are likely to be the same or very similar and I can see there being a lot of code duplication. I really only want the actual template for these subviews to be different with everything else the same.

2. Conditional Logic in Model View - Multiple Template Methods

var ModelView = Backbone.View.extend({
    ....
    render: function() {
        if(this.model.get("type") ==== "wotsit") {
            this.$el.html(this.wotsitTemplate(this.model.attributes));
        } else if(this.model.get("type") === "oojamaflip") {
            this.$el.html(this.oojamaflipTemplate(this.model.attributes));
        }
    },
    wotsitTemplate: _.template('<h2>{{ title }}</h2>'),
    oojamaflipTemplate: _.template('<h3>{{ title }}</h3>')
});

Pros There is only one view for the model. All of the events etc are handles in one view rather that being duplicated.

Cons I actually quite like this way, but I would be very interested to hear some other peoples options on it.

1
I strongly suggest you take a look at Marionette or Chaplin source code to get meaningfull answers.Or you can drop backbone views all together and use React or Vue libs,that support composite views.Backbone View component is just...bad.mpm
@mpm I really liked Marionette starting out. Now, I start to get a feeling it's not nearly as 'robust' as I'd like - and I mean this from a code standpoint and not necessarily a performance one.Sharadh

1 Answers

4
votes

Option #1 = polymorphism

Option #2 = switch statement

Switch statements are useful if you never (or rarely) have to add new types, but you might want to add new methods. Imagine you want to add a validate method to ModelView, which checks view input for that kind of model for correctness and reports the results to the user. With option #2, you'd just add one new method that switches on the model type (just like the render method) to handle validation.

Now let's assume we already have a render method and a validate method, and we want to handle a new type of model, a thingamajig. With option #2, you'd have to add logic to both render and validate. Now imagine we don't have just 2 methods, but 10 — option #2 gets real complicated real quick when you have to handle new types. But if we followed option #1, then no matter how many methods there were, we'd only have to create a new view in one place, and update CollectionView to map the new type to the new view.

Most of the time polymorphism (option #1) is the clean way to go. It lets you separate all logic specific to a given type and put it one place, making it easy to handle new types.

Keeping DRY

If you're worried with option #1 that you'll end up with a lot of duplicate code between all the views, don't forget that it's easy to make Views that inherit from other Backbone Views:

var ItemView = Backbone.View.extend({
    initialize: function() {
        /* some common logic for all views */
    }
});
var WotsitView = ItemView.extend({
    template: _.template('<h2>{{ title }}</h2>')
});
var OojamaflipView = ItemView.extend({
    template: _.template('<h3>{{ title }}</h3>')
});