Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

backbone.js events not firing after re-render

[EDIT: I solved earlier problem by calling delegateEvents(), but shouldn't have to. Re-posting w/more info.]

I have a View that when rendered has a login button on it with a click event attached. First render all works: click on button and upon successful ajax call the login prompts disappear and welcome view (LoggedInView) is displayed. But, if I navigate back to this View later (#foo) the UI renders but the event association is gone without manually forcing the issue by calling delegateEents().

What happened that my events didn't re-associate themselves?

LoginView = Backbone.View.extend({
    template: _.template($("#loginTemplate").html()),
    initialize: function(stuff,router) {
        _.bindAll(this,'render','rc','gotProfile');
        this.model.bind("rc",this.rc)
        this.router = router;
    },
    events: {
        'click .loginButton': 'login'
    },  
    render: function() {
        $(this.el).html(this.template(this.model.toJSON()));
        $(this.el).find(".actionButton").button();  // Button
//      this.delegateEvents(this.events);  // NEEDED!  Re-wire events on re-render
        return this;
    },
    rc: function(code) {
        switch(code) {
            case errCodes.USER_NOT_FOUND:   this.notFound(); break;
            case errCodes.OK:               this.loginOk(); break;
            default:                            this.otherErr(); break;
        }
    },
    login: function() {
        clearErrors( $('[rel="req"]') );
        var okReq = validate( $('#login [rel="req"]'), validateReq );
        var okEmail = validate( [$('#uid')], validateEmail );
        if( okReq && okEmail ) {
            this.model.set({'uid':$('#uid').val().trim(),     'pwd':$('#pwd').val().trim()});
            this.model.fetch();
        }
    },
    notFound: function() {
        validate( [$('#uid'),$('#pwd')], function(){return[false,"Invalid user / password"]} );
    },
    otherErr: function() {
        validate( [$('#uid'),$('#pwd')], function(){return[false,"Please contact support for help logging in."]} );
    },
    loginOk: function() {
        this.profile = new Profile({uid:this.model.get('uid'),token:this.model.get('key')});
        this.profile.bind("rc",this.gotProfile)
        this.profile.fetch();
    },
    gotProfile: function() {
        this.router.navigate('/',true);
    }
});

LoggedInView = Backbone.View.extend({
    template: _.template($("#loggedInTemplate").html()),
    uList: new ProfileList(),
    initialize: function() {
        _.bindAll(this,'render','renderUserList','renderUser');
        this.model.bind('show', this.render);
        this.uList.bind('rc', this.render);
    },
    events: {
        'click #ufa': 'getUsersForHouse'
    },
    render: function() {
        $(this.el).html(this.template(this.model.toJSON()));
        this.renderUserList();
//      this.delegateEvents(this.events); // NEEDED!  Re-wire events on re-render
        return this;
    },
    renderUserList: function() {
        $(this.el).find('ul').empty();
        this.uList.each(this.renderUser);
    },
    renderUser: function(aUser) {
        $(this.el).find('#userList').append("<li>"+aUser.get('person').firstName+"</li>");
    },
    getUsersForHouse: function() {
        this.uList.fetch(this.model.get('token'),"house");
    }
});

Main = Backbone.Router.extend({
    routes: {
        'foo': 'foo',
        '*all': 'home'
    },
    initialize: function() {
        this.token = new Token();
        this.loginView = new LoginView({model:this.token},this);
    },
    foo: function(){  // naving here looses click event on login button
        $('#login').empty();
        $("#login").append(this.loginView.render().el);
    },
    home: function() {
        $('#login').empty();
        if( this.loginView.profile == null ) 
            $("#login").append(this.loginView.render().el);
        else {
            this.loggedInView = new LoggedInView({model:this.loginView.profile});
            $("#login").append(this.loggedInView.render().el);
        }
    }
});
like image 751
Greg Avatar asked Sep 08 '11 13:09

Greg


People also ask

How do I trigger an event in Backbone JS?

js trigger Event is used to invoke or callback the function for the given event or a space-delimited list of events. The subsequent arguments will be passed along to the event callbacks in order to trigger it. Parameter Values: event: It is used to bind an object with an event.

How does backbone js work?

Backbone. js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.

Why use backbone js?

BackboneJS allows developing of applications and the frontend in a much easier way by using JavaScript functions. BackboneJS provides various building blocks such as models, views, events, routers and collections for assembling the client side web applications.


3 Answers

You empty the the #login div. As the jQuery doc says:

To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.

So you are effectively removing events from your views. I prefer to use detach because it keeps events. http://api.jquery.com/detach/

You can implement a show/hide in your views that will deal with this.

like image 126
Julien Avatar answered Sep 20 '22 04:09

Julien


I found a solution to this, because I was having the same problem, actually detach() is not the solution because it will bring back the old DOM elements for you which is not useful.

this is what you need to do

render: function(){ this.$el.empty();  ......  this.$el.appendTo($DOM);   } 

if you use the .html() method your events will not trigger. you need to attach the newly created HTML to the DOM, by using appendTo().

by the way this only happened to me if you are using tagName and className to build your view dynamically.

like image 36
Mahdi Pedram Avatar answered Sep 20 '22 04:09

Mahdi Pedram


I'm facing the same problem. The way I solved it for now is completely re- instantiating the View object with new MyView() this ensures that the events are rebound.

Hope that helps?

like image 20
amit Avatar answered Sep 17 '22 04:09

amit