Event is not binding while creating dynamic controls - reactjs

When I add an event using the first code snippet, the event is firing. But when i do the same thing with a variable, the event is not firing/binding. can any one help me?
var ProductTable = React.createClass({
ChangeSearch : function(event){console.log("Text changed");},
render: function() {
return (<input type="text"onChange= {this.ChangeSearch} />);
}
});
same code with variable:
var ProductTable = React.createClass({
var headerFilters =[];
ChangeSearch : function(event){console.log("Text changed");},
render: function() {
headerFilters.push(<th><input type="text" onChange={this.ChangeSearch} /></th>);
return ({headerFilters});
}
});
First one and the second one are looping through and adding the text boxes. With the variable only i will be able to generalize the code. I have removed the looping from the first code snippet to reduce the complexity.

If you need to render list of input elements its better to create simple element and map over it in Parent. Read react documentation about dynamic children
jssfidle:
code sample:
var InputTextElement = React.createClass({
changeHandler: function(event) {
console.log('text.changed' + event.target.value)
},
render: function() {
return (
<input type="text" name={this.props.name} onChange={this.changeHandler} />
)
}
})
var ProductTable = React.createClass({
render: function() {
var headerFilters =[{id: 1, name: "test"}, {id:2, name: "another"}];
return (
<div>
{ headerFilters.map(function(data) {
return <InputTextElement key={data.id} name={data.name} />;
})}
</div>
);
}
});
setTimeout(function() {
ReactDOM.render( <ProductTable />
, document.getElementById('element'))
} ,700);
working like a charm.

Related

React js attach click event on every single span element

I have the following react code at: http://codepen.io/AlexanderWeb00/pen/ZOWyNr
I am trying to attach for every span a click event that allows to console.log the correspondent number that you have clicked on:
var one = [],
two = [];
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: one,
val2: two,
action: ''
}
},
handleClick: function() {
var val1 = this.state.val1,
val2 = this.state.val2;
if(!$('.action').length){
val1.push(this.props.numbers);
console.log(val1[0].join(" "));
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
var ff =this.handleClick;
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={ff} className={classes}>{el}</span>);
});
return (
<div className="row">
{numbers}
</div>
);
}
});
what I am getting instead is 123456789 as opposed to 5 if I only click on number 5.
As stated in the comment, you are pushing the whole numbers arrays into val1, therefore it will always display the whole list. To solve it we need to pinpoint the clicked span, get its number, and add it to the list.
The first method would be using jQuery, and the eventArg that the onClick sends.
Once the span is clicked our handleClick is called, with the click event data as a parameter. We will extract the clicked span from the event arg, ie eventArg.target, and get its text using jQuery (ie $(eventArg.target).text()).
To specifically solve the issue you could:
var one = [],
two = [];
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: one,
val2: two,
action: ''
}
},
handleClick: function(e) {
var val1 = this.state.val1,
val2 = this.state.val2;
if(!$('.action').length){
val1.push($(e.target).text());
console.log(val1.join(" "));
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
var ff =this.handleClick;
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={ff} className={classes}>{el}</span>);
});
return (
<div className="row">
{numbers}
</div>
);
}
});
https://jsfiddle.net/omerts/zm0bzmwp/
Another option is to wrap the span's number in a colsure. In other words, create an anonymous function, which will have the number as part of its scope object. Since closures are out of the scope of this question, for further reading: Closures
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: [],
val2: [],
action: ''
}
},
handleClick: function(number) {
if(!$('.action').length){
const updatedList = this.state.val1.slice(); // clone the array, and add the new number
updatedList.push(number);
console.log(updatedList.join(" "));
this.setState({val1: updatedList});
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={() => {this.handleClick(el)}} className={classes}>{el}</span>);
}, this);
return (
<div className="row">
{numbers}
</div>
);
}
});
https://jsfiddle.net/omerts/590my903/
Second option is better, since you should not be mutating state, unless you are using this.setState. If you don't really need it as part of your state, just make val1, and val2 data memebers of your SingleButton.

How to listen for DOM updates in a react.js component?

I am trying to figure out why a react.js component is not updating when I call this.setState() from it. I would like to try to listen for all DOM updates as a way of debugging this. Is this possible?
var ChangingProgressBar = React.createClass({
getInitialState: function(){
return {'percentage': 0,
'mood':'progress-bar-info'}
},
componentWillMount: function(){
self = this;
setTimeout(function(){
self.setState({'mood', 'progress-bar-danger'});
}, 2000);
},
render: function(){
return (
<div className='progress-bar {this.state.mood}'
role='progressbar' aria-valuenow='{this.state.percentage}'
aria-valuemin='0' aria-valuemax='100'
style="{{width: this.state.percentage + '%'}}">
{this.state.percentage}%
</div>
)
})
})
self.setState({'mood', 'progress-bar-danger'});
should be
self.setState({'mood': 'progress-bar-danger'});

Parent inform children

I've got a list (parent) with lots of items (children). There's a huge save button on the list. When the user presses that button, I'd like to have all the childrens save() method to be fired.
Usually, I'd use a custom event for this (or some PubSub implementation), but I wonder: What's the React.js way to achieve this?
The parent should have the state and render children with props. This is just a example, not tested.
Parent:
var Parent = React.createClass({
getInitialState: function(){
return {array: []}
},
handleChange: function(key, value){
// here will you get key and value from child input.
this.setState({array[key]: value});
},
saveAll: function(){
// this.state.array have all data, lets save it :)
},
render: function(){
var self = this;
var items = [];
this.state.array.map(function(item, key){
items.push(<Child handleChange={self.handleChange} index={key} value={item} />);
});
return (<div>{items} <button onClick={this.saveAll}>SaveAll</button> </div>
}
});
Child:
var Child = React.createClass({
onChange: function(event){
this.props.handleChange(this.props.index, event.target.value);
},
render: function(){
return (<li><input value={this.props.value} onChange={this.onChange} />)
}
});

ReactJS can't access "this" methods

I am trying to pass a method to a child component to handle onclick events.
I saw a lot of examples online, but I can't get it working.
When I am inside the render function of the parent and trying to pass "this.handleClick" to the child, handleClick is undefined.
Have a look at render method of ThumbList:
var Thumb = React.createClass({
handleClick: function() {
console.log(this)
console.log('handleClick of Thumb')
this.props.onClick()
},
render: function() {
return(
<div className="thumb" key={this.props.thumb.url}>
<a href='#' onClick={this.handleClick}>
<img src={'/img/rings/thumbs/'+this.props.thumb.url+'_thumb.jpg'} alt="Image">
</img>
</a>
</div>
);
}
});
var ThumbList = React.createClass({
handleClick: function (id) {
console.log('click of ThumbList');
},
loadFromServer: function() {
$.ajax({
url: 'rings/imgs/5',
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('rings/imgs/5', status, err.toString());
}.bind(this)
});
},
getInitialState: function(){
return {data: [] };
},
componentDidMount: function(){
this.loadFromServer();
setInterval(this.loadFromServer, 2000);
},
render: function() {
var handlefunc=this.handleClick
var thumbsNodes = this.state.data.map(function(thumb) {
console.log(this.handleClick) // is Undefined!
console.log(handlefunc) // is working
return (
<Thumb thumb={thumb} key={thumb.url} onClick={handlefunc.bind(this,thumb.url)}/>
);
});
return(
<div className="col-md-1 col-md-offset-1" id='thumbs'>
{thumbsNodes}
</div>
);
}
});
Any idea what I might be missing?
If you're using a compiler like Babel as part of your development workflow, I'd suggest using arrow functions:
var thumbsNodes = this.state.data.map((thumb) => {
console.log(this.handleClick);
return <Thumb thumb={thumb} key={thumb.url}
onClick={this.handlefunc.bind(this,thumb.url)}/>;
});
As you can see, it's a nice compact syntax. The arrow function will preserve the this context for you. The Babel compiler produces JavaScript that uses a closure:
var thumbsNodes = this.state.data.map(function(thumb) {
var _this = this;
console.log(_this.handleClick);
return <Thumb thumb={thumb} key={thumb.url}
onClick={_this.handlefunc.bind(_this,thumb.url)}/>;
});
this is undefined because the map callback does not know what it is. The simplest way to solve this is to pass a second argument, and it will use that as this in the callback:
var thumbsNodes = this.state.data.map(function(thumb) {
console.log(this.handleClick)
return <Thumb thumb={thumb} key={thumb.url} onClick={handlefunc.bind(this,thumb.url)}/>
}, this)
More: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
You need to grab a reference to this so it is the correct context when you call it.
Try
render: function()
{
var handlefunc = this.handleClick; // i assume this is just for debugging the issue
var self = this;
var thumbsNodes = this.state.data.map(function(thumb)
{
console.log(self.handleClick) // note the use of `self`
});
}

Backbone View Events Not Firing, EL exists

I've been through similar discussions and still had no clue why my events are not firing. It seems I lack some fundamental understanding about how backbone events work.
Here is my code:
(function() {
MP.IndicatorView = Backbone.View.extend({
template: JST['backbone/templates/indicator'],
className: 'row indicator',
initialize: function() {
console.log(this);
console.log(this.el);
_.bindAll(this, 'collapse');
},
events: {
"click .control" : "collapse"
},
collapse: function (e) {
console.log('shit');
var $el = $( e.target );
$el.toggleClass( 'expand' );
var $panelToPoke = $el.
parents( '.top-container' ).
siblings( '.bottom-container' )
, $parentPanel = $panelToPoke.parents('.indicator')
, isVisible = $panelToPoke.is( ':visible' );
$parentPanel.toggleClass( 'expanded' );
isVisible ? $panelToPoke.slideUp() : $panelToPoke.slideDown();
},
render: function () {
// Don't show offers that have no transactions
if (this.model.get('transactionCount') > 0) {
this.$el.html( this.template(this.model.for_template()) );
}
return this.$el;
}
});
MP.IndicatorsView = Backbone.View.extend({
el: '#indicators',
render: function () {
var view;
var subViews = this.collection.reduce(function ( memo, indicator ) {
view = new MP.IndicatorView({ model: indicator });
memo += view.render().html();
return memo
}, '');
this.$el.html( subViews );
return this;
}
});
})();
How views are instantiated:
var indicators = new MP.Indicators( coordinator.dump() );
var indicatorsView = new MP.IndicatorsView({ collection: indicators });
indicatorsView.render();
Template:
Views:
<div class="row indicator">
<div class='top-container'>
<ul class="inline-list mainpanel">
<li>
<div class='control expand'></div></li>
<li class="state <%= is_active ? 'active' : 'expired' %>"></li>
Here is a working solution http://jsfiddle.net/hcKv2/
The main problem was that you are used .html() instead of backbone view element .$el inside of your indicatorsView.render() method.
You need to return this inside of render methods to achieve chain pattern, but it is not necessary it is ok if you are returning $el in child view.
render: function () {
var view;
var subViews = this.collection.reduce(function ( memo, indicator ) {
view = new MP.IndicatorView({ model: indicator });
//memo += view.render().html();
this.$el.append( view.render() );
// or this.$el.append( view.render().$el );
// if you return this inside render method
return memo
}, '');
//this.$el.html( subViews );
return this;
}
Have a good coding.
some reference to furthur explain why:
http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/

Resources