简体   繁体   中英

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

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.

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');
        }
    }

});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM