Backbone 子视图未正确呈现

Backbone subview not rendered properly

我开始使用 backbone.js 开发一个网站,在尝试了整个上午之后,我完全陷入了以下问题。

我这里只输出相关代码

我有一个名为 Navigator 的视图,其中包含一个记录集合(最初为空):

var NavigatorView = Backbone.View.extend({
    template: JST['app/scripts/templates/Navigator.ejs'],

    tagName: 'div',

    id: '',

    className: 'saiNavigator',

    events: {},

    initialize: function () {
        this.currentRecords = new RecordsCollection();
        this.currentRecords.on('reset', this.onRecordsCollectionReseted.bind(this));
    },
    onRecordsCollectionReseted: function(){
        this.render();
    },
    render: function () {
        var tplResult = this.template({
            computeTemplate: this.computeTemplate,
            records: this.currentRecords
        });
        this.$el.html(tplResult);
    },
    onDOMUpdated: function(){
        var me = this;
        var data = {
            device : 'web',
            gridId : this.model.get('gridId'),
            filterId : this.model.get('filterId')
        };

        $.ajax({
            url: App.getTokenedUrl() + '/task/getGridData.'+this.model.get('taskId')+'.action',
            success: me.onRecordReceived.bind(me),
            statusCode: {
                500: App.handleInternalError
            },
            type: 'GET',
            crossDomain: true,
            data : data,
            dataType: 'json'
        });
    },
    onRecordReceived: function(result){
        var newRecords = [];
        for(var i = 0; i < result.items.length; i++){
            var newRecord = new RecordModel(result.items[i]);
            newRecords.push(newRecord);
        }            
        this.currentRecords.reset(newRecords);
    }
});

我有一个名为 dossier 的视图,html 是

<div id="dossier1" class="dossier">
  <div id="dossier1-navContainer" class="navigatorContainer"/>   
  <div class="pagesNavigatorContainer"/>
  <div class="pagesContainer"/>
  <div class="readOnlyFiche"/>
</div>

当我第一次渲染档案时(我只渲染一次)我在下面的渲染函数中创建了导航器

render: function () {
    this.$el.html(this.template({
        uniqBaseId: this.id,
        className: this.className
    }));

    var nav = this.navigator = new NavigatorView({
        model : this.model,
        id: this.id+'navigator',
        el: $('#'+this.id+'-navContainer')
    });

    this.navigator.render();
    //We notify the navigator that it's ready. This will allow the nav to load records
        nav.onDOMUpdated();
    }
}

如我们所见,我将“#dossier1-navContainer”id 提供给导航器,以便他在那里呈现

所以,这是它的工作原理。当我呈现档案时,它会创建一个导航器并将其插入 DOM。完成后,我通知导航器它可以通过 ajax 请求从服务器加载数据。当我收到答案时,我用传入的记录重置了数据集。

在导航器渲染函数中的 this.$el.html(tplResult) 之前,我输出结果字符串。

第一次

<div class="items"></div> 

第二次拿到记录的时候是

<div class="items">
    <div>item1</div>
    <div>item2</div>
    <div>item3</div>
</div> 

所以模板生成是正确的。但是,当第二次渲染发生时,this.$el.html(tplResult) 会 NOTHING。如果我在浏览器中查看 DOM NOTHING CHANGED

但是,如果我将此行替换为

$('#dossier1-navigator').html(tplResult) 

有效。这意味着第一次 $('#dossier1-navigator') 和 this.$el 是同一个对象,第二次不是。

我不知道为什么它在第二次使用标准 this.$el.

时不起作用

求助!!

提前致谢

编辑:在与 Seebiscuit 进行了大量讨论之后,我添加了有助于回答问题的几行内容

newTask.render();
var taskHtml = newTask.$el.html();
$('#mainTaskContainer').append(taskHtml);

我认为问题在于您没有使用传递给 navigatorView 的内容;

在你的 navigatorView 中试试这个:

initialize:function(el) {
    this.$el=el
    ...
}

如果有帮助请告诉我

我的预感是您遇到了绑定问题。我建议你更换

 this.currentRecords.on('reset', this.onRecordsCollectionReseted.bind(this)); },

在您的 initialize 中,其中:

this.listenTo(this.currentRecords, "reset", this.render);

无需特别绑定。 Backbone 的 listenTo 将回调出价给设置侦听器的 Backbone 对象(this.listenTo 中的 this)。还有一个额外的好处,当您关闭视图(通过调用 this.remove())时,它会删除侦听器,并帮助您避免僵尸视图。

试试吧。

经过与 seebiscuit 无数分钟的讨论,我们得出了解决方案。问题全部出在 $el 元素的定义上。正式定义将其定义为

A cached jQuery object for the view's element. A handy reference instead of re-wrapping the DOM element all the time

从标准缓存的角度来看,这实际上不是很准确。从我的角度来看,至少缓存的原则是在没有值的情况下查找值,否则就使用它。然而,在这种情况下,情况并非如此。正如 Seebiscuit 告诉我的那样,

Because when you first bound this.$el = $(someelement) this.$el will always refer to the return of $(someelement) and not to $(someelement). When does the difference matter? When the element is not in the DOM when you do the assignment

所以实际上,$el 持有选择器第一次查找的结果。因此,如果第一次查找未命中,那么它将永远不会成功!即使后面添加了元素。

我这里的错误是将主 dossierView 添加到 DOM after 渲染其 NavigatorView 子视图。如果 $el 是真正的缓存,我本可以找到解决方案,因为 ajax 回调中的第二次渲染会找到该元素。按照目前 $el 的工作方式,我一无所有。

结论:确保在您尝试呈现子视图时 DOM 正确呈现视图的每个部分。