I am new to javascript and Backbone.js. I would like to bind a custom listener to a Backbone view on initialization. For example, I would like to achieve something like this:
var CampaignListView = Backbone.View.extend({
initialize: function(){
this.on("customFunc")
},
customFunc: function() {
if (this.$el.scrollTop() == 500 ) {
console.log("this has occurred, time to do stuff")
}
}
)}
That whenever a user scrolls to a specified position, I can execute some code.
Thanks.
I'm assuming this is something you want to happen when the window scrolls, no? In that case you have a few options:
The first is something a bit more familiar, and close to what you have, using the remove method to cleanup that binding:
var CampaignListView = Backbone.View.extend({
initialize: function(){
// You probably want to add an identifier to the event name, like 'scroll.campainlist' or something
$(window).on('scroll', _.bind(this.customFunc, this));
},
customFunc: function() {
if (this.$el.scrollTop() == 500 ) {
console.log("this has occurred, time to do stuff")
}
},
remove: function() {
$(window).off('scroll');
Backbone.View.prototype.remove.call(this);
}
)}
The other option is to use an Event Aggregator like so:
var vent = _.extend({}, Backbone.Events);
$(window).on('scroll', function(ev) {
vent.trigger('window:scroll', ev);
});
var CampaignListView = Backbone.View.extend({
initialize: function(){
this.listenTo(vent, 'window:scroll', this.customFunc);
},
customFunc: function() {
if (this.$el.scrollTop() == 500 ) {
console.log("this has occurred, time to do stuff")
}
}
)}
You can make use of delegateEvents in Backbone Views. For example you could attach to the scroll event of your view, but of course it will fire with every scroll. I put together a quick and very simple jsfiddle here based on your example. Below is some of the JavaScript. Notice the use of the events{} in the code.
var CampainListView = Backbone.View.extend({
el: '#list',
events: {
'scroll': 'customFunc'
},
initialize: function() { },
render: function() {
this.$el.html(sampleHtml);
return this;
},
customFunc: function() {
// console.log('scrolling... top is ' + this.$el.scrollTop());
if (this.$el.scrollTop() >= 100 ) {
console.log("this has occurred, time to do stuff")
}
}
});
var view = new CampainListView();
view.render();
Related
I have one question about Backbone.View and its delegateEvents. You can read in docs here about extend method. Using this method, you can "override the render function, specify your declarative events" etc.
I have a question about declarative events or delegateEvents (not sure how should I call it). They are described here. And here is an example:
var DocumentView = Backbone.View.extend({
events: {
"dblclick" : "open",
"click .icon.doc" : "select",
"contextmenu .icon.doc" : "showMenu",
"click .show_notes" : "toggleNotes",
"click .title .lock" : "editAccessLevel",
"mouseover .title .date" : "showTooltip"
},
open: function() {
window.open(this.model.get("viewer_url"));
},
select: function() {
this.model.set({selected: true});
},
...
});
As you can see, you can add different events on specific objects in template DOM. Like click or mouseover. So, having this template:
<foo></foo>
{#myplayers}
<player class="normal" value="{player}" style="{style}"></player>
{/myplayers}
You can add different click event on every single player, like this:
events: {
'click player': 'playerClick'
},
playerClick: function( e ) {
var playerValue = e.currentTarget.getAttribute( 'value' );
// HERE: e.currentTarget I've particular player
}
My question: Can I declare render event in similar way as click event? I want to catch event when single player (not the whole list) is rendered. I need to get e.currentTarget there and change its css a little bit.
I'm looking for something like this:
events: {
'render player': 'playerRendered'
},
playerRendered: function( e ) {
var playerValue = e.currentTarget.getAttribute( 'value' );
// HERE: e.currentTarget I've particular player
}
How can I do this? Because render in delegateEvents, doesn't work:/
Maybe in the initialize function within your view you can have a listenTo with the render. Something like that:
var view = Backbone.View.extend({
className: 'list-container',
template: _.template($('#my-template').html()),
initialize: function () {
this.listenTo(this, 'render', function () {
console.info('actions');
});
},
render: function() {
this.$el.html(this.template(this));
return this;
},
});
And then:
var myView = new view();
myView.render();
myView.trigger('render');
$('#container').html(myView.el);
var View = Backbone.View.extend({
events: {
'render .myselector': 'playerRendered'
},
playerRendered: function( e ) {
console.log("arguments");
var playerValue = e.currentTarget.getAttribute( 'value' );
// HERE: e.currentTarget I've particular player
},
render:function(){
console.log("render");
this.$el.html("<div class='myselector'></div>");
}
});
var view = new View();
view.render();
and you can trigger with Jquery trigger
this.$(".myselector").trigger("render");
or outside your view
view.$(".myselector").trigger("render");
In the following code:
var AppView = Backbone.View.extend({
events:{
"click .button":"cancel"
},
cancel:function() {
console.log("do something...");
},
onSomeEvent: function() {
this.$el.undelegate('.button', 'click', this.cancel);
}
});
var view = new AppView();
I need to undelegate this.cancel handler from elements with 'button' classes. Unfortunately this.$el.undelegate in onSomeEvent method doesn't work.
How could I remove that event handler?
try something like:
....
onSomeEvent: function() {
this.delegateEvents(
_(this.events).omit('click .button')
);
}
update:
do you mean like:
this.events[event] = "someEvent";
//call delegateEvents() on the view to re-bind events
this.delegateEvents();
I got this weird thing yesterday. I try several of time to fix this problem. When I came back the page same twice, my app trigger alert multiple times, depends how many times I visit the page. I already done some research regarding to this 'zombie' thing and memory lack through this site and internet, but I found dead end. It's already 2 days can't fix this issue.
Backbone.js events in my views being triggering multiple times
Backbonejs event occurring multiple times
http://blog.bigbinary.com/2011/08/18/understanding-bind-and-bindall-in-backbone.html
http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
My code
View page
initialize: function() {
$(window).scroll(function() {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
alert("bottom!");
}
});
this.bind("reset", this.updateView());
},
render: function() {
this.$el.html(notificationListViewTemplate);
},
updateView: function() {
console.log("clear");
this.remove();
this.render();
}
router
showNotificationList: function(actions) {
var notificationListView = new NotificationListView();
this.changePage(notificationListView);
},
Why it happen?
Calling View.remove will indeed undelegate events set by the view
remove view.remove()
Removes a view from the DOM, and calls stopListening to remove any bound events that the view has listenTo'd.
but it can only do so on the events it know about : the ones set by the events hash or by calling this.listenTo
You set up a scroll listener but you never remove it, which means the past views will keep listening : see this demo of your predicament http://jsfiddle.net/nikoshr/E6MQ6/
In this case, you can't use the hash of events so you have to take care of the cleaning up yourself, for example by overriding the remove method :
var V = Backbone.View.extend({
initialize: function() {
$(window).scroll(function() {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
console.log("bottom!");
}
});
},
render: function() {
},
updateView: function() {
console.log("clear");
this.remove();
this.render();
},
remove: function() {
Backbone.View.prototype.remove.call(this);
$(window).off('scroll'); // for example, will remove all listeners of the scroll event
}
});
And a demo http://jsfiddle.net/nikoshr/E6MQ6/1/
And a slightly less brutal removing of the scroll event, by using a namespaced listener :
var V = Backbone.View.extend({
initialize: function() {
$(window).on('scroll.'+this.cid, function() {
...
});
},
remove: function() {
Backbone.View.prototype.remove.call(this);
$(window).off('scroll.'+this.cid);
}
});
http://jsfiddle.net/nikoshr/E6MQ6/2/
I'm trying to remove my old carView and add the next one once the NEXT button is clicked.
Everything is coming out of a JSON file and is incrementing correctly but I want to view to also change.
Here's the code for my view:
window.CarContainerView = Backbone.View.extend({
el: $('#car-container'),
template:_.template($('#tpl-car-container').html()),
initialize:function () {
_.bindAll(this, 'clickNext');
this.car_number = 0;
this.car_model = this.model.get('carCollection').models[this.question_number];
this.question_view = null;
},
render:function () {
$(this.el).html(this.template());
this.car_view = new CarView({el: $(this.el).find('#car'), model: this.car_model});
this.question_view.render();
$('#next').bind('click', this.clickNext);
return this;
},
createNewCar: function () {
//build
console.log('createNewCar');
if(condition) {
//if the next button is pressed, clear screen and add new screen
}
},
clickNext: function () {
this.car_number++;
console.log(this.car_number);
createNewCar();
},
clickPrevious: function () {
}
});
Comments explain the changes. Basically, create a new CarView each time. And don't pass in the el to the view, else when you call remove that element will be gone. Instead, render the new view into #car each time.
window.CarContainerView = Backbone.View.extend({
el: $('#car-container'),
template:_.template($('#tpl-car-container').html()),
// use events hash instead of directly using jquery.
events: {
'click #next': 'clickNext'
},
initialize:function () {
// no need to use bindAll since using events hash
// binds the context of clickNext to this view already.
this.car_number = 0;
},
render:function () {
// use this.$el instead.
this.$el.html(this.template());
this.createNewCar();
return this;
},
createNewCar: function () {
if(this.car_view){
// cleanup old view.
this.car_view.remove();
}
// do some bounds checking here, or in clickNext... or both!
var car_model = this.model.get('carCollection').models[this.car_number];
// create a new view for the new car.
this.car_view = new CarView({model: car_model});
// render into #car instead of passing `el` into the view.
// let Backbone generate a div for the view, you dont need to
// set an `el` in the CarView either.
this.$('#car').html(this.car_view.render().el);
},
clickNext: function () {
this.car_number++;
this.createNewCar();
},
clickPrevious: function () {
}
});
I'd like to implement a reversible animation in Backbone, in the same way we do it in jquery :
$('a.contact').toggle(
function(){
// odd clicks
},
function(){
// even clicks
});
my question is how to do this in backbone's event syntax?
How to do I mimic the function, function setup?
events : {
'click .toggleDiv' : this.doToggle
},
doToggle : function() { ??? }
Backbone's view events delegate directly to jQuery, and give you access to all of the standard DOM event arguments through the callback method. So, you can easily call jQuery's toggle method on the element:
Backbone.View.extend({
events: {
"click a.contact": "linkClicked"
},
linkClicked: function(e){
$(e.currentTarget).toggle(
function() {
// odd clicks
},
function() {
// even clicks
}
);
}
});
I was looking for a solution to this problem and I just went about it the old fashioned way. I also wanted to be able to locate my hideText() method from other views in my app.
So now I can check the status of the 'showmeState' from any other view and run either hideText() or showText() depending on what I want to do with it. I have tried to simplify the code below by removing things like render and initialize to make the example more clear.
var View = Backbone.View.extend({
events: {
'click': 'toggleContent'
},
showmeState: true,
toggleContent: function(){
if (this.showmeState === false) {
this.showText();
} else {
this.hideText();
}
},
hideText: function() {
this.$el.find('p').hide();
this.showmeState = false;
},
showText: function() {
this.$el.find('p').show();
this.showmeState = true;
}
});
var view = new View();
Is the element you want to toggle within the view receiving the event? If so:
doToggle: function() {
this.$("a.contact").toggle()
}
I actually believe the only to do this using events is to add a trigger in order to keep the actual flow together. It seems a bit clumsy to be honest to have to use toggle in this way.
Backbone.View.extend({
events: {
"click .button": "doToggle"
},
doToggle: function(e){
var myEle = $(e.currentTarget);
$(e.currentTarget).toggle(
function() {
// odd clicks
},
function() {
// even clicks
}
);
myEle.trigger('click');
}
});
It's probably cleaner to just use
Backbone.View.extend({
el: '#el',
initalize: function() {
this.render();
},
doToggle: {
var myEle = this.$el.find('.myEle');
myEle.toggle(
function() {
// odd clicks
},
function() {
// even clicks
}
);
},
render: function(e){
//other stuff
this.doToggle();
return this;
}
});