Backbone: событие, потерянное при рендеринге

Я супер-вид кто отвечает за оказание суб-вид. Когда Я ... --3-->рендеринг на супер-вид все события суб-вид теряются.

вот пример:

var SubView = Backbone.View.extend({
    events: {
        "click": "click"
    },

    click: function(){
        console.log( "click!" );
    },

    render: function(){
        this.$el.html( "click me" );
        return this;
    }
});

var Composer = Backbone.View.extend({
    initialize: function(){
        this.subView = new SubView();
    },

    render: function(){
        this.$el.html( this.subView.render().el );             
    }
});


var composer = new Composer({el: $('#composer')});
composer.render();

когда я нажимаю на нажми на меня div событие запускается. Если я выполню composer.render() снова все выглядит довольно одинаково, но событие click не запускается больше.

Регистрация рабочая jsFiddle.

3 ответов


при этом:

this.$el.html( this.subView.render().el );

вы эффективно говорите это:

this.$el.empty();
this.$el.append( this.subView.render().el );

и empty убивает события на все внутри this.$el:

чтобы избежать утечек памяти, jQuery удаляет другие конструкции, такие как обработчики данных и событий, из дочерних элементов перед удалением самих элементов.

Итак, вы теряете delegate вызова, который связывает события на this.subView и SubView#render не перевяжите их.

вам нужно проскользнуть this.subView.delegateEvents() вызов this.$el.html() но нужно, чтобы это произошло после empty(). Вы могли бы сделать это так:

render: function(){
    console.log( "Composer.render" );
    this.$el.empty();
    this.subView.delegateEvents();
    this.$el.append( this.subView.render().el );             
    return this;
}

демо:http://jsfiddle.net/ambiguous/57maA/1/

или такой:

render: function(){
    console.log( "Composer.render" );
    this.$el.html( this.subView.render().el );             
    this.subView.delegateEvents();
    return this;
}

демо:http://jsfiddle.net/ambiguous/4qrRa/

вы можете remove и воссоздать this.subView при рендеринге и обойти проблему таким образом (но это может вызвать другие проблемы...).


здесь есть более простое решение, которое не сдувает регистрации событий в первую очередь:jQuery.detach().

http://jsfiddle.net/ypG8U/1/

this.subView.render().$el.detach().appendTo( this.$el );   

этот вариант, вероятно, предпочтительнее по соображениям производительности, хотя:

http://jsfiddle.net/ypG8U/2/

this.subView.$el.detach();

this.subView.render().$el.appendTo( this.$el );

// or

this.$el.append( this.subView.render().el );

очевидно, что это упрощение, которое соответствует примеру, где sub view является единственным содержимым родителя. Если это было действительно так, вы могли бы просто повторно отобразить представление sub. Если бы был другой контент, вы могли бы сделать что-то вроде:

var children = array[];

this.$el.children().detach();

children.push( subView.render().el );

// ...

this.$el.append( children );

или

_( this.subViews ).each( function ( subView ) {

  subView.$el.detach();

} );

// ...

кроме того, в исходном коде и повторяется в ответе @mu объект DOM передается в jQuery.html(), но этот метод документируется только как принятие строк HTML:

this.$el.html( this.subView.render().el );

документально подписи jQuery.html():

.html( htmlString )

http://api.jquery.com/html/#html2


при использовании $(el).empty() Он удаляет все дочерние элементы выбранного элемента и удаляет все на событий (и данные), которые привязан к любым (дочерним) элементам внутри выбранного элемента (el).

до держите события привязанными к на дочерние элементы, но все же удалите дочерние элементы, используйте:

$(el).children().detach(); вместо $(.el).empty();

Это позволит ваш взгляд на rerender успешно с событиями все еще связаны и работают.