在同一页面中容纳多个Backbone视图,模型和集合

时间:2016-11-16 16:35:48

标签: javascript backbone.js backbone-routing

我正在努力在同一页面中显示两个模型/集合。

    <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

问题

您的方面上方显示任务。我创建了两组代码来渲染任务 Facets ,但分别修改了变量名称。不幸的是,前者无法显示。

2 个答案:

答案 0 :(得分:1)

你制作了2个路由器,都是空路径。每条路由都在Backbone.history中注册,因此当facets路由器初始化时,其路由将覆盖任务路由器路由。

如何拥有多个路由器?

对于应用程序的范围,您应该首先创建一个路由器,然后在父视图中处理包含2个列表的页面。为该页面制作一种Layout视图,它将处理2个列表:

var Layout = Backbone.View.extend({
    template: _.template($('#layout-template').html()),
    // keep the selector strings in a simple object
    selectors: {
        tasks: '.task-container',
        facets: '.facet-container',
    },
    initialize: function() {

        this.view = {
            tasks: new TaskList(),
            facets: new FacetList()
        };
    },
    render: function() {
        this.$el.html(this.template());
        var views = this.views,
            selectors = this.selectors;
        this.$(selectors.tasks).append(views.tasks.render().el);
        this.$(selectors.facets).append(views.facets.render().el);
        return this;
    }
});

然后,只有一个路由器:

var Router = Backbone.Router.extend({
    routes: {
        "": "list",
    },
    list: function() {
        this.listContainerView = new Layout();
        $("body").html(this.listContainerView.render().el);
    }
});

这不能按原样使用您的代码,您必须自己将这些概念合并到您的应用中。

否则,如果你真的想要多个路由器,你必须明白他们不能共享路由,而且在任何时候,只能触发一条路由。

当您有多个路由器时,每个路由器都管理一个模块的路由。

var TaskRouter = Backbone.Router.extend({
    routes: {
        'tasks': 'taskList',
        'tasks/:id': 'taskDetails'
    }
    // ...snip...
});

var FacetsRouter = Backbone.Router.extend({
    routes: {
        'facets': 'facetList',
        'facets/:id': 'facetDetails'
    }
    // ...snip...
});

其他改进

编译模板一次

在扩展视图时,比每次初始化新视图时编译模板一次效率更高。

myapp.view.TasksContainer = Backbone.View.extend({
    // gets compiled once
    template: _.template($("#list_container_tpl").html()),

    initialize: function() {
        // not here, as it gets compiled for each view
        // this.template = _.template($("#list_container_tpl").html())
    },
});

避免使用全局jQuery函数

有关其他信息,请参阅What is the difference between $el and el

缓存jQuery对象

每当您想要选择视图元素的子元素时,请执行一次并将结果放入变量。

render: function(data) {
    this.$el.html(this.template);
    // I like to namespace them inside an object.
    this.elements = {
        $list: this.$('.task-list'),
        $search: this.$('.task-sorting')
    };

    // then werever you want to use them
    this.elements.$list.toggleClass('active');

    return this;
},

使用listenTo

避免bind / unbindon / off / once(别名)支持listenTo / stopListening / listenToOnce

listenTo is an improved version of bind解决了内存泄漏问题。

this.collection.bind("reset", this.render, this);
// becomes
this.listenTo(this.collection, "reset", this.render);

将一组对象传递给集合

myapp.collection.Tasks = Backbone.Collection.extend({
    model: myapp.model.Tasks
    // ...snip...
});

myapp.collection.tasks = new myapp.collection.Tasks([{
    completed: 0,
    name: "Clear dishes"
}, {
    completed: 1,
    name: "Get out the trash"
}, {
    completed: 0,
    name: "Do the laundry"
}, {
    completed: 1,
    name: "Vacuuming the carpet"
}]);

这就足够了,Backbone收集了其余部分。

答案 1 :(得分:1)

正如Emile在详细answer中提到的那样,你的问题是你正在使用相同的路由初始化多个路由器。

由于您似乎从Backbone开始,我会给您一个比创建复杂的布局(父)视图更简单的答案:

只需拥有一个特定路径的处理程序,并在其中初始化您的视图。

它看起来像是:

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
  }
});

您只需在同一路线中初始化2个视图。