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;
}
});
Related
I am using intl-tel-input to get peoples phone code and country. When they select the flag and code I have some jQuery with gets the country and code separately and populates hidden fields...
Here is the entire object which handles these operations:
var telInput = {
init: function(){
this.setTelInput();
this.cacheDOM();
this.bindEvents();
},
cacheDOM: function(){
this.$countryCode = $('input[name=country_code]');
this.$countryName = $('input[name=country_name]');
this.$country = $('.country');
},
bindEvents: function(){
this.$country.on('click', this.getTelInput.bind(this));
},
setTelInput: function(){
$('input[name=phone]').intlTelInput({
preferredCountries: [
'gb'
],
});
},
getTelInput: function(e){
var el = $(e.target).closest('.country');
var countryCode = el.attr('data-dial-code');
var countryName = el.find('.country-name').text();
this.$countryCode.val(countryCode);
this.$countryName.val(countryName);
},
}
Problem
The problem is the click event in the bindEvents method... the phone picker works but the onclick event is not being triggered to populate my hidden fields.
You need to make sure that you cache the DOM after it has been rendered. Here's a simplified example.
View on jsFiddle
var telInput = {
init: function(){
this.cacheDOM();
this.bindEvents();
},
cacheDOM: function() {
this.$country = $('.country');
},
bindEvents: function() {
this.$country.on('click', function() {
// console.log('Triggered');
});
}
}
$(document).ready(function() {
telInput.init();
});
Also, plugin's event countrychange could be useful in your case.
$("#country-select-element").on("countrychange", function(e, countryData) {
// do something with countryData
});
I would also suggest you to have a look at the public methods. It has some useful methods that you could use to achieve the same result.
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");
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();
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 am new to backbone and I am looking for a way for my button to be triggered when I press Enter as well as clicking. Currently showPrompt only executes on a click. What is the cleanest DRYest way to have it execute on pressing Enter as well, preferably only for that input field.
(function () {
var Friend = Backbone.Model.extend({
name: null
});
var Friends = Backbone.Collection.extend({
initialize: function (models, options) {
this.bind("add", options.view.addFriendLi);
}
});
var AppView = Backbone.View.extend({
el: $("body"),
initialize: function() {
this.friends = new Friends(null, {view: this});
},
events: {
"click #add-friend": "showPrompt",
},
showPrompt: function () {
var friend_name = $("#friend-name").val()
var friend_model = new Friend({ name:friend_name });
this.friends.add( friend_model );
},
addFriendLi: function (model) {
$("#friends-list").append("<li>" + model.get('name') + "</li>");
}
});
var appView = new AppView;
}());
Also where can I read more about this kind of event binding? Do backbone events differ from JS or jQuery events in how they're defined?
Assuming that you are using jQuery for DOM manipulation, you can create your own "tiny" plugin that fires the Enter event in the inputs. Put it in your plugins.js or whatever setup scripts file you have:
$('input').keyup(function(e){
if(e.keyCode == 13){
$(this).trigger('enter');
}
});
Now that you have created this "enter" plugin, you can listen to enter events this way:
events: {
"click #add-friend": "showPrompt",
"enter #friend-name": "showPrompt"
}
You can add one more event to your events hash in AppView.
events: {
"click #add-friend": "showPrompt",
"keyup #input-field-id" : "keyPressEventHandler"
}
Where #input-field-id is the one you want to add event on.
Then add eventHandler in AppView.
keyPressEventHandler : function(event){
if(event.keyCode == 13){
this.$("#add-friend").click();
}
}
NOTE : This code is not tested but you can think doing it in this way.
Have a look at this to understand how Backbone handles events in a View.