最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How should I unbind events in BackBone.js view that opens a SimpleModal dialog? - Stack Overflow

programmeradmin1浏览0评论

Every time I create a view and open the dialog, I get n sets of events where n is the number of times the dialog has been opened. In the sample below, each time I click fooButton I will get n button click events. I know I'm supposed to unbind events but this.undelegateEvents() isn't working.

From what I understand about the way SimpleDialog (and other dialog widgets work), the contents of the div are copied into another element when the dialog is created, which suggest that I should be able to capture the created element (say $dialog = this.$el.modal(); ) and then call undelegateEvents on that. This approach also isn't working.

Any ideas?

MyDialogView = Backbone.View.extend({
    initialize: function(options){
        this.render();
    },
    render: function() {

        this.$el.empty().append("<button id='fooButton'>Foo</button>");

        this.$el.modal({ "static": true });
    },
    events: {
        "click #fooButton": "fooPressed"
    },
    fooPressed: function() {
        alert("clicked");

        $.modal.close();
    }
});

$(function(){
    $("#openDialog").click(function() {
        new MyDialogView({el: $("#dialog") });
    });
});

​Thanks for your help!

Solved by switching to JQuery UI Dialog. See my answer below.

Every time I create a view and open the dialog, I get n sets of events where n is the number of times the dialog has been opened. In the sample below, each time I click fooButton I will get n button click events. I know I'm supposed to unbind events but this.undelegateEvents() isn't working.

From what I understand about the way SimpleDialog (and other dialog widgets work), the contents of the div are copied into another element when the dialog is created, which suggest that I should be able to capture the created element (say $dialog = this.$el.modal(); ) and then call undelegateEvents on that. This approach also isn't working.

Any ideas?

MyDialogView = Backbone.View.extend({
    initialize: function(options){
        this.render();
    },
    render: function() {

        this.$el.empty().append("<button id='fooButton'>Foo</button>");

        this.$el.modal({ "static": true });
    },
    events: {
        "click #fooButton": "fooPressed"
    },
    fooPressed: function() {
        alert("clicked");

        $.modal.close();
    }
});

$(function(){
    $("#openDialog").click(function() {
        new MyDialogView({el: $("#dialog") });
    });
});

​Thanks for your help!

Solved by switching to JQuery UI Dialog. See my answer below.

Share Improve this question edited Dec 24, 2012 at 4:17 Andy Hull asked Dec 23, 2012 at 21:10 Andy HullAndy Hull 1,8932 gold badges14 silver badges16 bronze badges
Add a ment  | 

6 Answers 6

Reset to default 3

Each time you instantiate your view, Backbone will call delegateEvents on your el:

delegateEvents delegateEvents([events])

[...] By default, delegateEvents is called within the View's constructor for you, [...]

So every time you do this:

new MyDialogView({el: $("#dialog") });

you're attaching a jQuery delegate to #dialog. Your problem is that you're not cleaning up after yourself, you should be removing the delegate when you shut down the dialog.

You should be calling undelegateEvents when you close your dialog:

fooPressed: function() {
    alert("clicked");
    this.undelegateEvents();
    $.modal.close();
}

Alternatively, you could create the view once and the call some method to pop it up as needed. With your set up, you'd drop the render call from initialize, all new MyDialogView(...) just once and save the view in a variable, and then my_dialog_view.render() as needed.

What version of Backbone are you using? As of 0.9.9:

Most importantly, Backbone events have two new methods: listenTo and stopListening. These are an inversion-of-control flavor of the usual on and off, and make it a little easier to clean up all events that an object is listening to on other objects. When you destroy Views with view.remove(), this will now be done automatically. Note that the usual rules about programming in a garbage collected language still apply.

I would guess that every-time you open your modal, your close button should call view.remove(). As of the latest version, Backbone should now unbind all events from the view, without you having to do it manually.

MyDialogView = Backbone.View.extend({
    initialize: function(options){
        this.render();
    },
    render: function() {

        this.$el.empty().append("<button id='fooButton'>Foo</button>");

        this.$el.modal({ "static": true });
    },
    events: {
        "click #fooButton": "fooPressed",
        "click #close": "closeView"
    },
    closeView: {
      this.remove();
      $.modal.close();
    },
    fooPressed: function() {
        alert("clicked");
    }
});

How about using jQuery's off method?

unbindEvents : function(){
    this.$el.off();
}

What if you'll not create new instance of MyDialogView each time on '#openDialog' click, but just render existing one?

MyDialogView = Backbone.View.extend({
    initialize: function(options){
        //this.render(); // don't need to call it here
    }
    // ...
});

$(function(){
    var myDialogView = new MyDialogView({
        el: $("#dialog")[0]
    });
    $("#openDialog").click(function() {
        myDialogView.render();
    });
});

My colleague Anton found a solution by replacing Simple Modal with JQuery UI Dialog as that returns an element that can be safely assigned to this.$el (Simple Modal could not). On open:

that.$el = $(that.$el.dialog({
    modal: true,
    resizable: false,
    "beforeClose": function (dialog) {
        that.stopListening();
        that.undelegateEvents();
    }
}));

Thanks again to everyone whom tried to help, hope this helps someone in the future.

That problem by using this in wrong scope, so in this case this is referenced to a method not view instance. Here is the way I have done before to remove an event of view and It works well.

var JobContainerNumberView = Backbone.View.extend({
events: {
        "focusout input.txtNumber": "checkBookingExists"
    },
    checkBookingExists: function() {
        var _this = this;
        var this_input = _this.$('input.txtNumber'); // text box where container is
        var booking_number = this_input.val();
        if ( booking_number != '') {
            $.post('booking/booking_exists', {'booking_number': booking_number},
                function(data,status,xhr) {
                if (data.exists) {
                    alert(data.message);
                    // stop only focusout event for the input
                    $(_this.el).off('focusout', 'input.txtNumber');
                    //remove all events on view
                    //_this.undelegateEvents();
                }
            },'json');
        }
    }

});

发布评论

评论列表(0)

  1. 暂无评论