Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstracting logic in Backbone js

I'm developing a Backbone application using Marionette and I need some help to organize the logic in the code.

Currently I have a few views where I handle really similar logic, I want to abstract this to avoid repetition:

View1

onRender: function() {
    var pluginData = this.$("selector1").plugin();
    var pluginResult = this.handlePluginData(pluginData);
    this.doSomethingWithResult1(pluginResult);
}

View2

onRender: function() {
    var pluginData = this.$("selector2").plugin();
    var pluginResult = this.handlePluginData(pluginData);
    this.doSomethingWithResult2(pluginResult);
}

Etc

Note: handlePluginData do the same thing, doSomethingWithResultN it's different but can be abstracted with a few params.

So the questions are:

  • How should I abstract this?, I thought of extending from a BaseView class and adding the logic there, but I don't know if there's a better way.

  • It's okay to add a custom Model class which handles the calculation?. I've been using rails for a while and I'm used to Model === Table in database.

Thank you very much!

like image 846
nicosantangelo Avatar asked Nov 13 '22 01:11

nicosantangelo


1 Answers

I think Backbone View class is abstract enough. All you have to do is to pass different options when you create new View instance.

And I found that you place the calculating logic in View's render method. Because Backbone is a MVC-based framework, so logic should place in Model and View register event handler which is responsible for rendering layout when Model fire event which view is interested.

In my opinion, you could add a Model which handle calculate and redefine your view.

My simple example:

1.Define a model which is respond for logic calculating:

var MathModel = Backbone.Model.extend({
    result: 0,
    initialize: function(){
        console.log("calculate target: "+this.get("selector"));
        console.log("calculate method: "+this.get("method"));
        var number = this.handlePluginData();
        this.doSomethingWithResult(number);
    },
    handlePluginData: function(){
        return $(this.get("selector")).text();
    },
    doSomethingWithResult: function(number){
        if(this.get("method")==="square"){
            this.set({result:(number*number)});
        }else{
            this.set({result:(number*number*number)});
        }

    }
});

2.Redefine the View class:

View will register a listener for model "result" data change event and render a initial layout according to the model you assigned.

var AbstractView = Backbone.View.extend({
        initialize: function(){
            this.listenTo(this.model,"change",this.onModelChange);
            this.number = this.model.get("result");
            this.render();
        },
        render: function(){
            this.$el.html(this.number); 
        },
        onModelChange: function(model){
            this.number = model.get("result");
            this.render();
        },
        plusOne:function(){
            this.model.doSomethingWithResult(this.model.handlePluginData());
        }
    });

3.Pass different options to model when you instantiate a new View.

var view1 = new AbstractView({el:"#result1",model:new MathModel({selector:".square",method:"square"})});
var view2 = new AbstractView({el:"#result2",model:new MathModel({selector:".cubic",method:"cubic"})});

4.HTML:

<div id="select-target">
    <span class="square">2</span>
    <span class="cubic">2</span>
    <button id="plus-one">+1</button>
</div>
<div id="result">
    <span id="result1"></span>
    <span id="result2"></span>
</div>

5.Plus-one button click event handler:

You could observe how View re-render it's layout when model is changed.

$("#plus-one").click(function(){
        $(".square").text(Number($(".square").text())+1);
        $(".cubic").text(Number($(".cubic").text())+1);
        view1.plusOne();
        view2.plusOne();
    });

Hope this is helpful for you.

like image 139
Chickenrice Avatar answered Nov 14 '22 23:11

Chickenrice