web-dev-qa-db-ja.com

バックボーン:再レンダリングでイベントが失われる

レンダリングを担当するsuper-Viewがいるsub-Viewsre-rendersuper-Viewのすべてのイベントをサブビューは失われます。

これは例です:

_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();
_

click medivをクリックすると、イベントがトリガーされます。 composer.render()をもう一度実行すると、すべてが同じように見えますが、クリックイベントがトリガーされなくなりました。

working jsFiddle を確認します。

37
fguillen

これを行うと:

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

あなたは効果的にこれを言っています:

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

および empty _this.$el_内のすべてのイベントを強制終了します。

メモリリークを回避するために、jQueryは、要素自体を削除する前に、データやイベントハンドラーなどの他の構成要素を子要素から削除します。

したがって、_this.subView_のイベントをバインドするdelegate呼び出しを失うと、_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_を再作成し、その方法で問題を回避できます(ただし、他の問題が発生する可能性があります...)。

65
mu is too short

最初の場所でイベント登録を吹き飛ばさない簡単な解決策があります: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 );
_

これは明らかに、サブビューが親の唯一のコンテンツである例に一致する簡略化です。それが本当の場合は、サブビューを再レンダリングするだけで済みます。他のコンテンツがある場合、次のようなことができます。

_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

11
JMM

$(el).empty()を使用すると、選択した要素のすべての子要素が削除されますANDはALLを削除しますイベント(およびデータ)にバインドされています任意の(子)要素選択した要素の内部(el)。

イベントのバインドを維持する子要素でも、子要素を削除するには、次を使用します。

$(el).children().detach();の代わりに$(.el).empty();

これにより、ビューがまだバインドされて機能している状態でビューを正常に再レンダリングできます。

0
AmpT