Search code examples
javascriptbackbone.jsbackbone-routing

Accommodate multiple Backbone views, models and collections in the same page


I am struggling with displaying two models/collections in the same page.

    <body>

<div id="mainContainer">
    <div id="contentContainer"></div>
</div>
<div id="mainContainer2">
    <div id="contentContainer2"></div>
</div>

<script id="list_container_tpl" type="text/template">
<div class="grid_5 listContainer">
    <div class="box">
        <h2 class="box_head grad_colour">Your tasks</h2>
        <div class="sorting">Show: <select id="taskSorting"><option value="0">All Current</option><option value="1">Completed</option></select>
            <input class="search round_all" id="searchTask" type="text" value="">
        </div>
        <div class="block">
            <ul id="taskList" class="list"></ul>
        </div>
    </div>
</div>
</script>

<script id="list2_container_tpl" type="text/template">
<div class="grid_5 mylistContainer">
    <div class="box">
        <h2 class="box_head grad_colour">Your facets</h2>
        <div class="sorting">
            %{--Show: <select id="taskSorting"><option value="0">All Current</option><option value="1">Completed</option></select>--}%
            <input class="search round_all" id="searchFacet" type="text" value="">
        </div>
        <div class="block">
            <ul id="facetList" class="list"></ul>
        </div>
    </div>
</div>
</script>


<script id="task_item_tpl" type="text/template">
<li class="task">
    <h4 class="name searchItem">{{ name }}</h4>
</li>
</script>
<script id="facet_item_tpl" type="text/template">
<li class="facet">
    <h5 class="label searchItem">{{ label }}</h5>
</li>
</script>

<script>
    var myapp = {
        model: {},
        view: {},
        collection: {},
        router: {}
    };
    var facetsSearch = {
        model: {},
        view: {},
        collection: {},
        router: {}
    };
</script>

<script src="underscore-min.js"></script>
<script src="handlebars.min.js"></script>
<script src="backbone-min.js"></script>
<script>
    /* avoid */
    _.templateSettings = {
        interpolate: /\{\{(.+?)\}\}/g
    };
</script>
<script>
    // model.tasks.js
    myapp.model.Tasks = Backbone.Model.extend({
        default:{
            completed: 0,
            name: ""
        },
        //url:"/js/libs/fixtures/task.json"
    });
    var tasks1 = new myapp.model.Tasks({
            completed: 0,
            name: "Clear dishes"
        }
    );
    var tasks2 = new myapp.model.Tasks({
            completed: 1,
            name: "Get out the trash"
        }
    );
    var tasks3 = new myapp.model.Tasks({
            completed: 0,
            name: "Do the laundry"
        }
    );
    var tasks4 = new myapp.model.Tasks({
            completed: 1,
            name: "Vacuuming the carpet"
        }
    );

    // collection.tasks.js
    myapp.collection.Tasks = Backbone.Collection.extend({
        currentStatus : function(status){
            return _(this.filter(function(data) {
                return data.get("completed") == status;
            }));
        },
        search : function(letters){
            if (letters == "") return this;

            var pattern = new RegExp(letters,"gi");
            return _(this.filter(function(data) {
                return pattern.test(data.get("name"));
            }));
        }
    });
    myapp.collection.tasks = new myapp.collection.Tasks([tasks1, tasks2, tasks3, tasks4]);

    // route.tasks.js
    myapp.router.Tasks = Backbone.Router.extend({
        routes: {
            "": "list",
        },
        list: function(){
            this.listContainerView = new myapp.view.TasksContainer({
                collection: myapp.collection.tasks
            });
            $("#contentContainer").append(this.listContainerView.render().el);
            this.listContainerView.sorts()
        }
    });
    myapp.router.tasks = new myapp.router.Tasks;

    <!-- render views -->
    myapp.view.TasksContainer = Backbone.View.extend({
        events: {
            "keyup #searchTask"     : "search",
            "change #taskSorting"   : "sorts"
        },
        render: function(data) {
            $(this.el).html(this.template);
            return this;
        },
        renderList : function(tasks){
            $("#taskList").html("");

            tasks.each(function(task){
                var view = new myapp.view.TasksItem({
                    model: task,
                    collection: this.collection
                });
                $("#taskList").append(view.render().el);
            });
            return this;
        },
        initialize : function(){
            this.template = _.template($("#list_container_tpl").html());
            this.collection.bind("reset", this.render, this);
        },
        search: function(e){
            var letters = $("#searchTask").val();
            this.renderList(this.collection.search(letters));
        },
        sorts: function(e){
            var status = $("#taskSorting").find("option:selected").val();
            if (status == "") status = 0;
            this.renderList(this.collection.currentStatus(status));
        }
    });
    myapp.view.TasksItem = Backbone.View.extend({
        events: {},
        render: function(data) {
            $(this.el).html(this.template(this.model.toJSON()));
            console.log(this.model.toJSON(), "became", this.template(this.model.toJSON()));
            return this;
        },
        initialize : function(){
            this.template = _.template($("#task_item_tpl").html());
        }
    });
</script>

<script>
    // model.facets.js
    facetsSearch.model.Facets = Backbone.Model.extend({
        default: {
            id: 0,
            label: "",
            facetValues: []
        }
    });

    var facet1 = new facetsSearch.model.Facets({
        id: 1,
        label: "Organism",
        facetValues: ["Orga1", "Orga2"]
    });
    var facet2 = new facetsSearch.model.Facets({
        id: 2,
        label: "Omics",
        facetValues: ["Omics1", "Omics2"]
    });
    var facet3 = new facetsSearch.model.Facets({
        id: 3,
        label: "Publication Date",
        facetValues: ["2016-11-01", "2016-11-02"]
    });

    // collection.facets.js
    facetsSearch.collection.Facets = Backbone.Collection.extend({
        search : function(letters){
            if (letters == "") return this;

            /**
             * the g modifier is used to perform a global match (find all matches rather than stopping after the first match).
             * Tip: To perform a global, case-insensitive search, use this modifier together with the "i" modifier.
             */
            var pattern = new RegExp(letters, "gi");
            return _(this.filter(function(data) {
                return pattern.test(data.get("label"));
            }));
        }
    });
    facetsSearch.collection.facets = new facetsSearch.collection.Facets([facet1, facet2, facet3]);

    // route.facets.js
    facetsSearch.router.Facets = Backbone.Router.extend({
        routes: {
            "": "list",
        },
        list: function(){
            this.mylistContainerView = new facetsSearch.view.FacetsContainer({
                collection: facetsSearch.collection.facets
            });
            console.log("Facet collection: ", facetsSearch.collection.facets);
            $("#contentContainer2").append(this.mylistContainerView.render().el);
            this.mylistContainerView.sorts()
        }
    });

    facetsSearch.router.Facets = new facetsSearch.router.Facets;

    facetsSearch.view.FacetsContainer = Backbone.View.extend({
        events: {
            "keyup #searchFacet" : "search",
            "change #facetSorting": "sorts"
        },
        render: function(data) {
            $(this.el).html(this.template);
            return this;
        },
        renderList : function(facets){
            $("#facetList").html("");

            facets.each(function(facet){
                var view2 = new facetsSearch.view.FacetsItem({
                    model: facet,
                    collection: this.collection
                });
                $("#facetList").append(view2.render().el);
            });
            return this;
        },
        initialize : function(){
            this.template = _.template($("#list2_container_tpl").html());
            this.collection.bind("reset", this.render, this);
        },
        search: function(e){
            var letters = $("#searchFacet").val();
            this.renderList(this.collection.search(letters));
        },
        sorts: function(e){
            /*var status = $("#taskSorting").find("option:selected").val();
             if (status == "") status = 0;
             this.renderList(this.collection.currentStatus(status));*/
        }
    });
    facetsSearch.view.FacetsItem = Backbone.View.extend({
        events: {},
        render: function(data) {
            $(this.el).html(this.template(this.model.toJSON()));
            console.log(this.model.toJSON(), "became", this.template(this.model.toJSON()));
            return this;
        },
        initialize : function(){
            this.template = _.template($("#facet_item_tpl").html());
        }
    });

</script>
<script>
    Backbone.history.start();
</script>
</body>

Display Tasks and Facets

The problem

To display Tasks above Your facets. I created two bunches of codes to render Tasks and Facets but modified the variable names respectively. Unfortunately, the former cannot be displayed.


Solution

  • As Emile mentioned in his detailed answer, your problem is that you're initializing multiple routers with the same route.

    Since you seem to be beginning with Backbone, I'll give you a simpler answer than creating a complicated Layout (parent) view:

    Just have a single handler for the specific route, and initialize both your views inside it.

    It'll look something like:

    myapp.router = Backbone.Router.extend({
      routes: {
        "": "list",
      },
      list: function() {
        this.listContainerView = new myapp.view.TasksContainer({
          collection: myapp.collection.tasks
        });
        $("#contentContainer").append(this.listContainerView.render().el);
    
        this.listContainerView.sorts(); //this can be done inside the view
        this.mylistContainerView = new facetsSearch.view.FacetsContainer({
          collection: facetsSearch.collection.facets
        });
        $("#contentContainer2").append(this.mylistContainerView.render().el);
        this.mylistContainerView.sorts(); //this can be done inside the view
      }
    });
    

    You simply initialize 2 views in the same route.