如何在更新集合时更新模板的一部分?

How to update part of a template when a collection is updated?

我试图在更新集合时仅更新 Backbone 视图使用的模板的一部分。下面的代码成功执行了 search()render_list() 方法。此外,console.log(html) 显示完整模板的 html。但是当我执行 replaceWith 时,它会将选择器替换为空。如果我将 $(selector,html) 替换为字符串(即:'test'),它会成功替换为 'test'。

因此,出于某种原因,$(selector, html) 选择器没有按预期执行。更奇怪的是,浏览器请求更新的 html 选择器中的图像,即使更新的 html 的 none 被插入到文档中。

define([
    'jquery',
    'underscore',
    'backbone',
    'collections/tracks',
    'collections/genres',
    'text!templates/search_view_title.html',
    'text!templates/search_view.html'
],function($,_,Backbone,Tracks_collection,Genres_collection,Search_view_title_template,Search_view_template){
    var Search_view = Backbone.View.extend({
        el: $('#app'),
        events: {
            'change #genre_select': function(){this.search('genre')},
            'click #search_btn': function(){this.search('search')}
        },
        template: _.template(Search_view_template),
        initialize: function(){
            // SET SOME IMPORTANT LAYOUT SETTINGS
            $('#pagetitle').html(_.template(Search_view_title_template));
            $('body').css('padding-top','124px');

            this.genre_collection = new Genres_collection();
            this.listenTo(this.genre_collection,'update',this.render);
            this.genre_collection.fetch();

            this.collection = new Tracks_collection();
            this.listenTo(this.collection,'update',this.render_list);

        },
        search: function(searchtype){
            switch(searchtype){
                case 'genre':
                    console.log('genre changed');
                    this.collection.fetch({
                        data: {limit: 30, type:'genre',genre_id:$('#genre_select').val()}
                    });
                break;
                case 'search':
                    console.log('search changed');
                    this.collection.fetch({
                        data: {limit: 30, type:'search',keyword:$('#keyword').val()}
                    });
                break;
            }
            console.log(this.collection);

        },
        render_list: function(){
            var that = this;
            console.log('render list');
            var html = that.template({genres: this.genre_collection.models,tracks: this.collection.models});
            console.log(html);
            var selector = '#tracklist';
            console.log($(selector,html));
            that.$el.find(selector).replaceWith($(selector,html));
            return this;
        },
        render: function(){
            // MAKE 'THIS' ACCESSIBLE
            var that = this;
            console.log('render');

            that.$el.find('#container').html(that.template({genres: this.genre_collection.models}));

            return this;
        }
    });

    return Search_view;

});

手头没有 HTML 模板,我只能假设。

更接近我的做法:

var Search_view = Backbone.View.extend({
    el: $('#app'),
    events: {
        'change #genre_select': 'onGenreChange',
        'click #search_btn': 'onSearchClick'
    },
    template: _.template(Search_view_template),
    initialize: function() {
        // SET SOME IMPORTANT LAYOUT SETTINGS
        $('#pagetitle').html(Search_view_title_template);

        // Do this in css
        $('body').css('padding-top', '124px');

        this.genre_collection = new Genres_collection();
        this.genre_collection.fetch();

        this.collection = new Tracks_collection();

        this.listenTo(this.genre_collection, 'update', this.render);
        this.listenTo(this.collection, 'update', this.render_list);

    },

    render: function() {
        console.log('render');

        this.$('#container').html(this.template({
            genres: this.genre_collection.models
        }));

        return this;
    },

    render_list: function() {
        console.log('render list');
        var html = this.template({
            genres: this.genre_collection.models,
            tracks: this.collection.models
        });
        console.log(html);
        var $selector = this.$('#tracklist');
        console.log($selector);
        $selector.replaceWith($(html));
        return this;
    },

    ////////////
    // events //
    ////////////
    onSearchClick: function() {
        console.log('search changed');
        this.collection.fetch({
            data: { limit: 30, type: 'search', keyword: $('#keyword').val() }
        });
    },

    onGenreChange: function() {
        console.log('genre changed');
        this.collection.fetch({
            data: { limit: 30, type: 'genre', genre_id: $('#genre_select').val() }
        });
    },
});

$('#pagetitle').html(_.template(Search_view_title_template));

_.template 函数 returns 一个函数,它本身 returns 调用时呈现的模板。

容易与this.template混淆,后者通常包含_.template的结果并准备调用(this.template(data))。


拆分你的回调,函数很便宜而且不必要switch很丑。

我把你的search变成了onGenreChangeonSearchClick


$('body').css('padding-top','124px');

尽量避免这种情况,使用 CSS 甚至内联 <style> 标记或内联 style="" 属性可以轻松完成。如果您需要它,因为它与动态行为有关,请在 css 文件中创建一个 class(例如 search-class),然后将 class 切换为 jQuery, 将 "design" 责任移出 js:

$('body').toggleClass('search-class');

var that = this;

只有在处理回调中上下文 (this) 不同的回调时才需要。在 Backbone 中,大多数情况下,它是可以避免的,因为 context 选项通常可用并在大多数情况下自动设置(如 events 回调)。


this.$el.find(selector)

这等同于this.$(selector)。只是一个小捷径。


.replaceWith($(selector,html));

replaceWith 需要一个 html 字符串或元素或数组或 jQuery.

$(selector, html) 需要 选择器和上下文 。您希望 $(html) 将 html 字符串转换为 jQuery 元素。