In my view, I have following bindings:
events:
{
'click #showallconsumers': 'showAllConsumers',
'submit form': 'submit',
'click #allconsumerstable tbody tr': 'selectConsumer',
},
In showAllConsumers function, I need to disable click on #showallconsumers anchor, fetch collection and rebind click event on #showconsumers after fetching finished.
showAllConsumers: function()
{
$(this.el).undelegate('#showallconsumers', 'click');
this.collection.fetch(({async: true, success : this.renderAllConsumers}));
},
renderAllConsumers: function(collection)
{
//i'm populating table with data from collection
$('#showallconsumers').bind('click', this.showAllConsumers);
},
When I click on #showallconsumers anchor, undelegate works, but when fetching is finished, .bind(...) can not rebind event. I also tried with .delegate(...).
The success function of fetch isn't getting called with the view as its context, so this.showAllConsumers isn't pointing to your function.
When calling fetch wrap the success function in Underscore's bind so that this points to your view:
this.collection.fetch(({async: true, success : _.bind(this.renderAllConsumers, this)}));
Related
I have a div generated by a backbone.js view. When the user clicks on this div, a class active is added to the div and the function addToSet is executed.
Problem: I want another function to be triggered when the View's div has the class active. However, my attempt shown below always cause addToSet function to run when its clicked.
Now, I remove 'click': 'addToSet' from the events function, leaving only 'click .active': 'removeFromSet'. Clicking on the div does not cause anything to happen! Is this because the event handler cannot select the div of the view itself, just the elements inside it?
Any idea how I can solve this problem? Thanks!
JS Code
SetView = Backbone.View.extend({
tagName: 'div',
className: 'modal_addit_set',
template: _.template( $('#tpl_modal_addit_set').html() ),
events: {
'click': 'addToSet',
'click .active': 'removeFromSet'
},
initialize: function(opts) {
this.post_id = opts.post_id;
},
render: function() {
$(this.el).html( this.template( this.model.toJSON() ) );
if(this.model.get('already_added'))
$(this.el).addClass('active');
return this;
},
addToSet: function() {
$.post('api/add_to_set', {
post_id: this.post_id,
set_id: this.model.get('id'),
user_id: $('#user_id').val()
});
},
removeFromSet: function() {
$.post('api/remove_from_set', {
post_id: this.post_id,
set_id: this.model.get('id')
});
}
});
Have you tried to use a :not(.active) selector for one of your event delegates? This may help differentiate between the two scenarios.
Something like this:
events: {
'click :not(.active)' : callback1
'click .active' : callback2
}
These events:
events: {
'click': 'addToSet',
'click .active': 'removeFromSet'
}
don't work and you sort of know why. From the fine manual:
Events are written in the format {"event selector": "callback"}. The callback may be either the name of a method on the view, or a direct function body. Omitting the selector causes the event to be bound to the view's root element (this.el).
So your 'click': 'addToSet' binds addToSet to a click on the view's el itself but 'click .active': 'removeFromSet' binds removeFromSet to a .active element inside the view's el.
I think the easiest solution is to have a single event:
events: {
'click': 'toggleInSet'
}
and then:
toggleInSet: function() {
if(this.$el.hasClass('active')) {
$.post('api/remove_from_set', {
post_id: this.post_id,
set_id: this.model.get('id')
});
}
else {
$.post('api/add_to_set', {
post_id: this.post_id,
set_id: this.model.get('id'),
user_id: $('#user_id').val()
});
}
}
You could use an instance variable instead of a CSS class to control the branching in toggleInSet if that makes more sense.
i want to call a method from view#1 which is already implemented in different view (view#2)..
how to achieve this in a nice n simple way.. using backbonejs.
App.Views.view1 = Backbone.View.extend({
events: {
'click .someclass1' : 'custom_method_1',
},
custom_method_1:function(e){
//now this method calls another method which is implemented in different view
custom_method_2();
},
});
App.Views.view2 = Backbone.View.extend({
events: {
'click .someclass2' : 'custom_method_2',
},
//// this method needs to be called from view1 also
custom_method_2:function(e){
},
});
If you search how to use the eventbus, you can do it like this:
// you can name the event 'custom_method_2' as you want
Backbone.Events.on('custom_method_2', App.Views.view2.custom_method_2);
Now you are listening to the event custom_method_2 on the Object Backbone.Events that you can consider as your eventsbus.
Then in view1:
custom_method_1:function(e){
//now this method calls another method which is implemented in different view
// custom_method_2();
Backbone.Events.trigger('custom_method_2', e);
},
In my router object, I created an event object to share among my views
I pass the event object to my views
I register events to this shared object like this
var productCatalogView = Backbone.View.extend({
initialize: function (options) {
//bind alert event to sharedEvents
options.sharedEvents.bind("alert", this.alert,this);
},
alert: function () {
alert('alerted');
}
});
//The following view triggers the alert event
var testView = Backbone.View.extend({
initialize: function (options) {
this.sharedEvents = options.sharedEvents;
},
events: {
'click #test': 'triggerAlert'
},
triggerAlert: function (e) {
this.sharedEvents.trigger("alert", null);
}
});
THE PROBLEM:
The problem I experience is that the first time I click on the button which triggers the alert event (second view), the alert event gets called once (good), this causes the first view to be re-rendered by triggering the route passing search parameters, therefore creating the first view and binding the sharedEvents again, hence when I trigger the alert event a second time, it gets triggered twice (bad), the next time I repeat the same process, it gets triggered 3 times, and so on and so forth. I guess it has to do with the event binding in the first view, it occurs more than once, i.e each time the view is initialized (if I am correct)
please how can I make the binding of the event occur once.
Here is my router which shows how I initilze the views:
var Router = Backbone.Router.extend({
sharedEvents:_.extend({},Backbone.Events),
catalog: function (id) {
//....unecessary code left out
var productView = new ProductView({sharedEvents:this.sharedEvents});
this.renderView(productView);
this.renderView(new testView({sharedEvents: this.sharedEvents }));
}
renderView: function (view) {
if (null != this.currentView) {
this.currentView.undelegateEvents();
// this.currentView.remove();
}
this.currentView = view;
this.currentView.render();
}
});
I have tried this solution but problem persists, thanks
Try using Backbone.Events' listenTo method instead of the bind method. Then, in your renderView(), call this.currentView.remove instead of this.currentView.undelegateEvents.
Rationale:
I believe in your renderView() method, you are using undelegateEvents() thinking it releases all event listeners created by your view. It only releases events bound on to your view's $el element. However, using remove() on the view releases events bound to the $el as well as events created using this.listenTo() (and this.listenOnce()).
Now once you render another view, the old currentView will be properly released and you'll only get one alert.
I have a Google Map where user can click (on the map everywhere), the click event opens a Bootstrap modal window, contained a form. My question is, how/where to handle this submit event to add a marker to the marker collection, save it to the db, etc.
Currently I have a Map View, that renders the google map, and adds an event listener for the click. Clicking on the map opens the Modal.
App.Views.Map = Backbone.View.extend({
...
initializeMap : function(){}
...
addMapEventlistener : function() {
google.maps.event.addListener(this.map, 'dblclick', function(event) {
var coords = event.latLng.toUrlValue();
var carray = coords.split(",");
var model = new Backbone.Model({ coords: carray });
var view = new App.Views.Modal({ model: model });
var $modalEl = $("#modal");
$modalEl.html(view.render().el);
$modalEl.modal();
});
}
App.Views.App = Backbone.View.extend({
initialize: function() {
var addMarkerView = new App.Views.AddMarker({ collection: App.markers });
}
});
// add marker view
App.Views.AddMarker = Backbone.View.extend({
el: '#addForm',
initialize: function() {
$('<input>', {
type: 'submit',
value: 'Submit',
class: 'smt'
}).appendTo(this.$el);
console.log('AddMarker init run'); // this echoed out
},
events: {
'submit' : 'addMarker'
},
addMarker: function(e) {
e.preventDefault();
alert('hello');
},
});
my guess is that the form rendered after the click event on the map, so I have to set backbone event listening somehow after the modal opens, and handle the form submission in a collection view, right?
You may want to use the events object to bind your listeners.
About when (re)binding your events. When the view is instantiated, the listeners specified in the events object will be bound to the view element. This implies that if your event targets a child element, the fact that the child exists or not at that moment doesn't matter. Now, you'll have to rebind your listeners in the special case where you change your view element without the setElement method.
Example:
<div id="#mydiv"></div>
And you want to bind an event on the buttons inside this div (you'll create some afterwards.).
Well, here's an example.
Ok, solved. My bad. I added events to the wrong view, I have to add it to the App.Views.Modal view (of course the event happens in the modal). Thanks for the time!
I wish to read a whole database table to fill a Backbone.js Collection, before updating a View.
I am using fetch and listening to the reset event.
My problem is the reset event fires up before the http request is made to the server.
My question is: how can I render the view AFTER the data is received back from the server on a fetch?
Here is a jsfiddle showing the problem (with a debugger placed at reset):
http://jsfiddle.net/GhaPF/16/
The code:
$(document).ready(function() {
var Item = Backbone.Model.extend({
urlRoot : './items'
});
var ItemList = Backbone.Collection.extend({
model: Item,
url: './items/',
});
var ItemListView = Backbone.View.extend({
el: 'body',
initialize: function(myitemList) {
this.itemlist = myitemList;
this.itemlist.bind('reset', this.debuggThis());
},
debuggThis: function() {
debugger;
},
render: function() {
},
events: {
"keypress #new-item": "createOnEnter"
},
createOnEnter: function(e) {
}
});
$("#new-item").focus();
var itemlist = new ItemList();
var myitemListView = new ItemListView(itemlist);
itemlist.fetch();
});
The following code works, but it just doesn't feel like proper backbone.js (MVC) code since it would be placed outside of the View definition:
itemlist.fetch().complete(function(){
Maybe the issue is this line:
this.itemlist.bind('reset', this.debuggThis());
Should actually be:
this.itemlist.bind('reset', this.debuggThis);
Your debugThis function was getting run at the time you set up the listener for the 'reset' event - not when the event is triggered. This was telling JavaScript that you wanted debugThis to return a callback function instead of having debugThis "be" the callback function.
Also, orangewarp's comment about passing 'this' as the third parameter is probably relevant too. Sot it would end up as:
this.itemlist.bind('reset', this.debuggThis, this);
That's strange. When you fetch() the reset event should be triggered AFTER your collection is populated. So I'm thinking the phenomena that reset happens before the http request is fired up may not be what you think it is.
Instead of using the complete... you could always just use the success callback option like this:
itemlist.fetch({
success: function() {
// Whatever code you want to run.
itemlist.debuggThis();
}
});
Also, when binding your reset you probably want this:
this.itemlist.bind('reset', this.debuggThis, this);