简体   繁体   中英

Backbone event handler doesn't work

I have two views -- a layout view, which is top-level, and a new form view, which acts as a child to the layout and renders within it. I have one event handler in the form view, which is supposed to create a new instance of my model according to the data from the input.

Here's the layout view:

var LayoutView = Backbone.View.extend({
    el: "#layout",
    render: function (view) {
        this.child = view;
        if (this.child) {
            this.child.remove();
        }
        this.$el.html(this.child.render().el);
        return this;
    }
});

and here's my form view:

var ResumeForm = Backbone.View.extend({
    events: {
        'click #create': 'createResume'
    },
    initialize: function () {
        this.template = _.template($('#new-resume').html());
    },
    render: function () {
        this.$el.html(this.template());
        return this;
    },
    createResume: function () {
        // getting values from template inputs and saving them to model
        var resume = new Resume({
            profession: $('#profession').val(),
            firstName: $('#firstname').val(),
            lastName: $('#lastname').val()
        });
        // saving a new model to collection instance
        resumes.add(resume);
        resume.save(null, {
            success: function (res) {
                console.log("POST resume id " + res.toJSON().id);
            },
            error: function () {
                console.log("Failed to POST");
            }
        });
    }
});

My form view renders within my layout view perfectly, but when I enter the values and click #create button, nothing happens -- neither saves the model nor logs any error mistake from my createResume method. I suspect that when rendering a form view in the layout view, the line this.$el.html(this.child.render().el); just destroys all event listeners, because if I add these listeners to layout view, it works.

Are there any ways to override this problem?

Backbone's view remove function undelegates the events bound to the el .

From the annotated source :

 remove: function() { this._removeElement(); this.stopListening(); return this; }, _removeElement: function() { this.$el.remove(); },

This has to do with jQuery .remove() function (emphasis mine):

Similar to .empty() , the .remove() method takes elements out of the DOM. Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed . To remove the elements without removing data and events, use .detach() instead.

If you call remove before reusing the view, you need to manually call the this.delegateEvents() to re-bind the events from the events hash and re-wire any event the view was listening through this.listenTo(...) .

But the best way to re-use views, without calling remove which calls stopListening , you can use setElement which undelegates the events, and change the view element to the passed element, then re-delegates the events.

 setElement: function(element) { this.undelegateEvents(); this._setElement(element); this.delegateEvents(); return this; },

Your LayoutView would become something like this:

var LayoutView = Backbone.View.extend({
    el: "#layout",
    render: function(view) {
        // if the view is different, make sure to undelegate the old view.
        if (this.child && this.child !== view) this.child.undelegateEvents();
        this.child = view;
        this.child.setElement(this.$el).render();
        return this;
    }
});

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