[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);
}
}
});
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.
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.
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.
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.
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.
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With