简体   繁体   中英

How can I make underscore templates interact with each other?

I have two types of templates in my Backbone.js project. The single-item template and list-of-items template.

Also, there are two types of views in the project: The single-item template and list-of-items respectively.

I use RequireJS to to make application modular. So, I guess, it's a good practice to keep templates separate too. But there's a problem: I cant' make my templates to "communicate". When I render my CollectionView, it makes in the page only one element - empty <ul id="spaceman-list"> and there's no collection of items in it. In my listview I use addItem functuion to wrap each collection item by itemView, but it's not working. The list just doesn't render in the dom.

I don't wanna use such messy things like _.each() , I wanna to keep my code clean.

Here's the both views code: list view

// app/js/views/spaceman-list.js

'use strict';

(function(w) {

    define([
        'jquery',
        'underscore',
        'backbone',
        '../models/spaceman',
        '../views/spaceman-item',
        '../collections/spaceman-collection',
        'text!../templates/spaceman-list.html'
    ], function($, _, Backbone, SpacemanModel, SpacemanItemView, SpacemanCollection, listTemplate) {
            var SpacemanListView = Backbone.View.extend({
                el: '#container',
                render: function() {
                    this.collection.on('reset', this.addItem, this);
                    this.collection.each(this.addItem, this);
                    var data = {
                        spacemen: this.collection.models
                    };
                    var compiled = _.template(listTemplate, data);
                    this.$el.html(compiled);
                    return this;
                },
                addItem: function(item) {
                    var newSpaceman = new SpacemanItemView({model: item});
                    this.$el.find('ul').append(newSpaceman.render());
                }
            });
            return SpacemanListView;
    });

   })(window);

And item view:

  // app/js/views/spaceman-item.js
 'use strict';

 (function(w) {

    define([
        'jquery',
        'underscore',
        'backbone',
        '../models/spaceman',
        'text!../templates/spaceman-item.html'
    ], function($, _, Backbone, SpacemanItem, itemTemplate) {
            var SpacemanItemView = Backbone.View.extend({
                el: '#spaceman-list',
                render: function() {
                    var data = {
                        spaceman: this.model
                    };
                    this.$el.html(_.template(itemTemplate, data));
                },
            });
            return SpacemanItemView;
    });

 })(window);

And also, as I told above, I keep my underscore templates separate: This is list template:

// app/js/templates/spaceman-list.html
<ul id="spaceman-list"></ul>

And this is item template:

// app/js/templates/spaceman-item.html
<li><% spaceman.get('name') %></li>

And also, I adding here my app entry point:

// app/js/application.js
'use strict';

(function(w) {
   define([
   'jquery',
   'underscore',
   'backbone',
   'collections/spaceman-collection',
   'views/spaceman-list'
   // Request router.js
   ], function($, _, Backbone, ItemsList, ListView) {
        var App = Backbone.View.extend({
                initialize: function(){
                // Pass in our Router module and call it's initialize function
                console.log('Yeeha!');
                var endurance = new ItemsList([
                                {name: 'Cooper'},
                                {name: 'Brend'},
                                {name: 'Romilly'},
                                {name: 'Doyle'},
                                {name: 'Mann'}
                            ]);

                console.log(endurance);

                    var view = new ListView({collection:endurance});

                    view.render();
                }
          });

            return App;
   });

})(window);

I'm just going crazy about this. How can I make separate templates to "see" each other?

You say this in SpacemanListView#addItem :

this.$el.find('ul').append(newSpaceman.render())

but SpacemanItemView#render doesn't return anything:

render: function() {
    var data = {
        spaceman: this.model
    };
    this.$el.html(_.template(itemTemplate, data));
},

SpacemanItemView also says:

el: '#spaceman-list'

so all the SpacemanItemView instances will be trying to use the same el when their render functions say this.$el.html(...) . Presumably your templates/spaceman-list.html contains <ul id="spaceman-list"> or something similar so #spaceman-list won't resolve to anything until the collection view has been placed on the page; but you're calling addItem before the collection view is on the page so the item views are all trying to render into the same nonexistent element.

The solution is twofold:

  1. render functions traditionally return this; so that the caller can $container.append(view.render().el) to put things on the page so do this.
  2. Using predefined el s (ie el: '#something' in a view definition) leads to all kinds ordering and event problems, things work better when each view creates and owns its own el .

addItem should look more like this:

addItem: function(item) {
    var newSpaceman = new SpacemanItemView({model: item});
    this.$('ul').append(newSpaceman.render().el);
}

Note the this.$ shortcut and the .el in the append call.

Then we rework the item view:

var SpacemanItemView = Backbone.View.extend({
    tagName: 'li',
    render: function() {
        var tmpl = _.template(itemTemplate);
        this.$el.html(tmpl({ spaceman: this.model }));
        return this;
    },
});

No more el but now we have tagName: 'li' , this will tell Backbone to create an empty <li> as the view's el . The el documentation covers how the various view properties are used to figure out what el will be.

Also note that the _.template(tmpl, data) form of _.template stopped working in Underscore 1.7.0 so I fixed that too.

You'd also adjust the templates/spaceman-item.html template so that it is just the content of the <li> :

<% spaceman.get('name') %>

I'd recommend similar changes to the collection view so that it creates and owns its el too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM