onClick event isn't firing on React Component - reactjs

I'm working on a component stub where I have a grid of tiles, each that require a click handler, and also a specific "new" tile that has a different click handler. I'm trying to get the click handlers working correctly, but the event never seems to fire.
Any ideas?
var grid = React.createClass({
onCreateNew: function () {
console.log("onCreateNew");
},
render: function () {
var tiles = [];
if (this.props.items) {
tiles = this.props.items.map(function (item, index) {
//create a tile for each item
});
}
//always append "new" tile for the last one
tiles.push(React.DOM.div({
onClick: this.onCreateNew, className: "tile new_tile"
},
React.DOM.div({ className: "plus", onClick: this.onCreateNew }, "+")
));
return React.DOM.div({ id: "flowsheetPane" }, tiles);
}
});

As commenters have mentioned, your code appears to work as expected in isolation.
React delegates to event handlers with a single event listener at the root of the DOM, so events need to propagate all the way to the top in order for React to call your function.
Could you have added some event listeners somewhere in the component hierarchy that are calling event.stopPropagation()? If you did this outside of React (e.g. natively or with jquery), it would cause the events to never reach the top of the DOM, and React would never have a chance to delegate out to your methods.

Related

events not firing after re-render in backbone.js

I am facing a problem while trying to click submit after re-render.
This is my view:
ShareHolderInfoView = Backbone.View.extend( {
template : 'shareholderinfo',
initialize: function() {
this.model = new ShareHolderInfoModel();
},
render : function() {
$.get("shareholderinfo.html", function(template) {
var html = $(template);
that.$el.html(html);
});
//context.loadViews.call(this);
return this;
},
events:{
"change input":"inputChanged",
"change select":"selectionChanged",
"click input[type=submit]":"showModel"
},
inputChanged:function(event){
var field = $(event.currentTarget);
var data ={};
data[field.attr('id')] = field.val();
this.model.set(data);
},
showModel:function(){
console.log(this.model.attributes);
alert(JSON.stringify(this.model.toJSON()));
}
});
This is my Router
var shareholderInfo, accountOwnerInfo;
App.Router = Backbone.Router.extend({
routes:{
'share':'share',
'joint':'joint'
},
share:function(){
$("#subSection").empty();
if(!shareholderInfo){
shareholderInfo = new ShareHolderInfoView();
$("#subSection").append(shareholderInfo.render().el);
} else{
$("#subSection").append(shareholderInfo.$el);
}
},
joint:function(random){
$("#subSection").empty();
if(!accountOwnerInfo){
accountOwnerInfo = new AccountOwnerInfoView();
$("#subSection").append(accountOwnerInfo.render().el);
} else{
$("#subSection").append(accountOwnerInfo.$el);
}
}
});
This is my HTML a div with id='subSection'.
if I check in console, I can able to see the events bound to that view.
Object {change input: "inputChanged", change select: "selectionChanged", click input[type=submit]: "showModel"}
But its not calling that showModel function afer i click submit. Please help.
Your fundamental problem is that you're improperly reusing views.
From the fine manual:
.empty()
Description: Remove all child nodes of the set of matched elements from the DOM.
[...]
To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.
So when you say:
$("#subSection").empty();
you're not just clearing out the contents of #subSection, you're also removing all event handlers attached to anything inside #subSection. In particular, you'll remove any event handlers bound to accountOwnerInfo.el or shareholderInfo.el (depending on which one is already inside #subSection).
Reusing views is usually more trouble than it is worth, your views should be lightweight enough that you can destroy and recreate them as needed. The proper way to destroy a view is to call remove on it. You could rewrite your router to look more like this:
App.Router = Backbone.Router.extend({
routes: {
'share':'share',
'joint':'joint'
},
share: function() {
this._setView(ShareHolderInfoView);
},
joint: function(random){
this._setView(AccountOwnerInfoView);
},
_setView: function(view) {
if(this.currentView)
this.currentView.remove();
this.currentView = new view();
$('#subSection').append(this.currentView.render().el);
}
});
If your views need any extra cleanup then you can override remove on them to clean up the extras and then chain to Backbone.View.prototype.remove.call(this) to call the default remove.
If for some reason you need to keep your views around, you could call delegateEvents on them:
delegateEvents delegateEvents([events])
Uses jQuery's on function to provide declarative callbacks for DOM events within a view. If an events hash is not passed directly, uses this.events as the source.
and you'd say things like:
$("#subSection").append(shareholderInfo.$el);
shareholderInfo.delegateEvents();
instead of just:
$("#subSection").append(shareholderInfo.$el);
I'd strongly recommend that you treat your views and cheap ephemeral objects: destroy them to remove them from the page, create new ones when they need to go on the page.

Prevent Backbone event from triggering more than once, how to check if event is registered already?

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.

Google Maps and Backbone.js event triggering

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!

Backbone event being called multiple times

I'm just getting my feet wet with Backbone, and I think I have an easy problem to solve. I have the following view which is a simple tab that when clicked opens up a panel and when closed goes back to a tab:
myApp.views.Support = {
Form: Backbone.View.extend({
initialize: function () {
this.el = $('#support');
this._ensureElement();
},
render: function () {
if (this.$el.hasClass('support-panel')) {
// close panel
this.$el.empty();
this.$el.removeClass('support-panel');
this.$el.addClass('support-button');
}
else {
// open and populate panel
var template = _.template(myApp.utils.RenderTemplate('support/default'), {});
this.$el.removeClass('support-button');
this.$el.addClass('support-panel');
this.$el.html(template);
}
return this;
},
closePanel: function () {
alert('close event fired');
},
events: {
'click #SubmitFormButton': 'submitForm',
'click #CloseSupportPanel': 'closePanel'
},
submitForm: function (event) {
alert('form submitted: ' + $('#message'));
}
})
}
Everything is working fine except that "closePanel" gets fired +2 times every time the click event happens. I assume it's some sort of cleanup I'm missing but I don't know what.
Likely its because the event is bubbling up. Try returning false.
I know this is an old question but it helped me realize what my issue was. Returning false as Daniel said works, but the root cause of my issue was having the jQuery selector twice in my markup, resulting in two jQuery objects being created thus the click event fires twice.

Translating the `on` pattern to Backbone's `event` hash

Inside my Backbone views, in the initialize function I do stuff like:
initialize: function () {
$(this.el).on('click', '.button', function () {
$(this).fadeTo(0.5);
}
}
This seems to go against Backbone's convention of using events. Rewriting with the events hash:
events: { 'click .button': 'fadeButton' },
fadeButton: function () {
$(this).fadeTo(0.5);
}
The problem is inside fadeButton's scope the value of this is not the same as when using .on(). What is the correct way of doing this using the events hash?
Like paul said, Backbone automatically sets the context for event callbacks to the view itself. So this in the callback will be the view instance.
So you can get the effect you intend by using the view's scoped selector function...
events: {
'click .button': 'fadeButton'
},
fadeButton: function () {
this.$('.button').fadeTo(0.5);
}
... but if you've got multiple elements with class "button" in your view, that'll fade all of them, in which case you can always use the event object that jQuery gives you to get the event target:
fadeButton: function (event) {
$(event.target).fadeTo(0.5);
}
You defined the events hash correctly.
And for every event handler defined, Backbone automatically sets the context to the view. So this within fadeButton is the view, and you will want to access the view's element.
The code below shows how you need to update the fadeButton function.
fadeButton: function () {
$(this.el).fadeTo(0.5);
}

Resources