I'm having a little problem while trying to create a checkbox that selects and deselects other individual checkboxes (select/deselect all) with React. I've read http://facebook.github.io/react/docs/forms.html and discovered that there are differences between controlled and not-controlled <input>s. My test code is as follows:
var Test = React.createClass({
getInitialState: function() {
return {
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" data-id={d.id} checked={d.selected} onChange={this.__changeSelection} />
{d.id}
<br />
</div>
);
});
return (
<form>
<input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks} />Global selector
<br />
{checks}
</form>
);
},
__changeSelection: function(e) {
var id = e.target.getAttribute('data-id');
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
},
__changeAllChecks: function(e) {
var value = this.refs.globalSelector.getDOMNode().checked;
var state = this.state.data.map(function(d) {
return { id: d.id, selected: value };
});
this.setState({ data: state });
}
});
React.renderComponent(<Test />, document.getElementById('content'));
The "Global selector" works as expected: when selected, all other checks are selected. The problem is that the __changeSelection() handler is not fired when one of the other checkboxes are clicked.
I don't know what is the proper way to make this work. Maybe React model is not the best one to model this kind of interaction? What could I do?
Thanks in advance
In your render function, the scope of this for the checks mapping function is different from render, which is the scope you need for __changeSelection, so this.__changeSelection won't locate a __changeSelection property. If you add a .bind(this) to the end of that mapping function, you can bind it's scope to the same this as render:
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" data-id={d.id} checked={d.selected} onChange={this.__changeSelection} />
{d.id}
<br />
</div>
);
}.bind(this));
On a side note, I would just pass the id to the handler function instead of assigning data-attributes. This will remove the need to locate that element in your handler:
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
{d.id}
<br />
</div>
);
}.bind(this));
Then update your __changeSelection function to pass in the id as the first arg and remove the attribute lookup line:
__changeSelection: function(id) {
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
}
Here is an example of it all put together, along with a jsfiddle for you to try it out:
/** #jsx React.DOM */
var Test = React.createClass({
getInitialState: function() {
return {
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
{d.id}
<br />
</div>
);
}.bind(this));
return (
<form>
<input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks} />Global selector
<br />
{checks}
</form>
);
},
__changeSelection: function(id) {
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
},
__changeAllChecks: function() {
var value = this.refs.globalSelector.getDOMNode().checked;
var state = this.state.data.map(function(d) {
return { id: d.id, selected: value };
});
this.setState({ data: state });
}
});
React.renderComponent(<Test />, document.getElementById('content'));
If you are dealing with checkboxes you can use the checkedLink attribute. Here is another possible implementation, that makes the global checkbox controlled (instead of uncontrolled in the current answers):
JsFiddle
var Test = React.createClass({
getInitialState: function() {
return {
globalCheckbox: false,
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
changeCheckForId: function(id,bool) {
this.setState(
{
data: this.state.data.map(function(d) {
var newSelected = (d.id === id ? bool : d.selected);
return {id: d.id, selected: newSelected};
}
)});
},
changeCheckForAll: function(bool) {
this.setState({
globalCheckbox: true,
data: this.state.data.map(function(d) {
return {id: d.id, selected: bool};
})
});
},
linkCheckbox: function(d) {
return {
value: d.selected,
requestChange: function(bool) { this.changeCheckForId(d.id,bool); }.bind(this)
};
},
linkGlobalCheckbox: function() {
return {
value: this.state.globalCheckbox,
requestChange: function(bool) { this.changeCheckForAll(bool); }.bind(this)
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input key={d.id} type="checkbox" checkedLink={this.linkCheckbox(d)} />
{d.id}
<br />
</div>
);
}.bind(this));
return (
<form>
<input type="checkbox" checkedLink={this.linkGlobalCheckbox()} />Global selector
<br />
{checks}
</form>
);
},
});
It is simpler to use checkedLink=this.linkState("checkboxValue") with LinkedStateMixin if the state to mutate is not deeply nested (like this is the case in this question)
Edit: checkedLink and valueLink are being deprecated but were recommmended in previous versions of React.
Related
I am trying to use the vue-select component for a dropdown list. So far I have written.
<template>
<div>
<v-select label="name" key="id" :v-model="selected" :reduce="data => data.id" :options="items" #input="update()" />
</div>
</template>
<script>
export default {
props: {
initial: {
type: [String, Number],
default: 0,
},
api_call: {
type: String,
required: true,
},
},
data(){
return {
value: this.initial,
items: [],
}
},
computed: {
selected: {
get() {
return this.value;
},
set(val) {
return this.value = val;
}
},
},
methods:{
update() {
console.log('selected', this.selected, this.value);
this.$emit('input', this.selected);
},
getData: function(){
axios.get('/api/' + this.api_call)
.then(function (response) {
this.items = response.data;
}.bind(this));
},
},
created(){
this.getData();
}
}
The dropdown list populates as intended and selecting an Item inserts it in the input filed. The two problems I have are
Neither the value nor the selected variables change when something is selected.
I am also passing in an initial value which I would like to be selected as the default in the list.
Remove the binding sign : from v-model directive
<v-select label="name" key="id" v-model="selected" :reduce="data => data.id" :options="items" #input="update()" />
and init your value like :
data(vm){//vm refers to this
return {
value: vm.initial,
items: [],
}
},
or :
data(){
return {
value: null,
items: [],
}
},
mounted(){
this.value=this.initial
}
I'm currently "testing the waters" with Reactjs. Based on their docs, I have whipped up a small project that I'm stuck with. So far, when the checkbox is checked, the state changes but....not sure how to change a state unchecked:
var Foo = React.createClass{(
getInitialState: function() {
return {
location: true,
}
},
onClick: function() {
this.setState({ location: false });
},
render: function() {
var inlineStyles = {
display: this.state.location ? 'block' : 'none'
};
return (
<div>
<input type="checkbox"
onClick={this.onClick}
/> show / hide bar
<hr />
<div style={inlineStyles}>
<p>bar</p>
</div>
</div>
);
}
)};
Do I need to use an if statement for the sort of thing I want? I need to this.setState.location: true when unchecked.
You need to read the state of the checkbox during a click, and apply that to your React state.
var Foo = React.createClass({
render: function() {
return <input type="checkbox" onClick={this.onClick} checked={!this.state.location}></input>
},
onClick: function(e) {
this.setState({location: !e.target.checked});
},
getInitialState: function() {
return {
location: true
};
}
});
I need to add the class active after clicking on the button and remove all other active classes.
Look here please: https://codepen.io/azat-io/pen/RWjyZX
var Tags = React.createClass({
setFilter: function(filter) {
this.props.onChangeFilter(filter);
},
render: function() {
return <div className="tags">
<button className="btn active" onClick={this.setFilter.bind(this, '')}>All</button>
<button className="btn" onClick={this.setFilter.bind(this, 'male')}>male</button>
<button className="btn" onClick={this.setFilter.bind(this, 'female')}>female</button>
<button className="btn" onClick={this.setFilter.bind(this, 'child')}>child</button>
<button className="btn" onClick={this.setFilter.bind(this, 'blonde')}>blonde</button>
</div>
}
});
var Kid = React.createClass({
render: function() {
return <ul>
<li>{this.props.name}</li>
</ul>
}
});
var List = React.createClass({
getInitialState: function() {
return {
filter: ''
};
},
changeFilter: function(filter) {
this.setState({
filter: filter
});
},
render: function() {
var list = this.props.Data;
if (this.state.filter !== '') {
list = list.filter((i)=> i.tags.indexOf(this.state.filter) !== -1);
console.log(list);
}
list = list.map(function(Props){
return <Kid {...Props} />
});
return <div>
<h2>Kids Finder</h2>
<Tags onChangeFilter={this.changeFilter}/>
{list}
</div>
}
});
var options = {
Data: [{
name: 'Eric Cartman',
tags: ['male', 'child']
},{
name: 'Wendy Testaburger',
tags: ['female', 'child']
},{
name: 'Randy Marsh',
tags: ['male']
},{
name: 'Butters Stotch',
tags: ['male', 'blonde', 'child']
},{
name: 'Bebe Stevens',
tags: ['female', 'blonde', 'child']
}]
};
var element = React.createElement(List, options);
React.render(element, document.body);
How do I make it better?
It is simple.
take a look at this
https://codepen.io/anon/pen/mepogj?editors=001
basically you want to deal with states of your component so you check the currently active one. you will need to include
getInitialState: function(){}
//and
isActive: function(){}
check out the code on the link
this is pretty useful:
https://github.com/JedWatson/classnames
You can do stuff like
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
or use it like this
var btnClass = classNames('btn', this.props.className, {
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
Taken from their site.
render() {
let className = 'menu';
if (this.props.isActive) {
className += ' menu-active';
}
return <span className={className}>Menu</span>
}
https://reactjs.org/docs/faq-styling.html
Since you already have <Tags> component calling a function on its parent, you do not need additional state: simply pass the filter to the <Tags> component as a prop, and use this in rendering your buttons. Like so:
Change your render function inside your <Tags> component to:
render: function() {
return <div className = "tags">
<button className = {this._checkActiveBtn('')} onClick = {this.setFilter.bind(this, '')}>All</button>
<button className = {this._checkActiveBtn('male')} onClick = {this.setFilter.bind(this, 'male')}>male</button>
<button className = {this._checkActiveBtn('female')} onClick = {this.setFilter.bind(this, 'female')}>female</button>
<button className = {this._checkActiveBtn('blonde')} onClick = {this.setFilter.bind(this, 'blonde')}>blonde</button>
</div>
},
And add a function inside <Tags>:
_checkActiveBtn: function(filterName) {
return (filterName == this.props.activeFilter) ? "btn active" : "btn";
}
And inside your <List> component, pass the filter state to the <tags> component as a prop:
return <div>
<h2>Kids Finder</h2>
<Tags filter = {this.state.filter} onChangeFilter = {this.changeFilter} />
{list}
</div>
Then it should work as intended. Codepen here (hope the link works)
you can also use pure js to accomplish this like the old ways with jquery
try this if you want a simple way
warning: this may not be the correct way to do it in react.
document.getElementById("myID").classList.add("show-example");
const activeState = (e)=>{
var id = e.target.id
const idArray = ["homeBtn","aboutBtn","servicesBtn","portfolioBtn","testmBtn","blogBtn","contactBtn"]
idArray.forEach((element)=> {
document.getElementById(element).classList.remove("active")
});
console.log(id);
document.getElementById(id).classList.add("active")
}
I want to have a react element on click execute another function from another js file which involves GET requests. How would I go about doing that.
React code:
/** #jsx React.DOM */
var SearchExample = React.createClass({
getInitialState: function(){
return { searchString: ' ' };
},
handleClick: function(event){
// do stuff in another file
},
handleChange: function(e){
this.setState({searchString:e.target.value});
},
render: function() {
var libraries = this.props.items,
searchString = this.state.searchString.trim().toLowerCase();
if(searchString.length > 0){
// We are searching. Filter the results.
libraries = libraries.filter(function(l){
return l.name.toLowerCase().match( searchString );
});
}
else
{
libraries = libraries.filter(function(l){
return l.popular.toLowerCase().match('true');
});
}
return <div>
<input type="text" value={this.state.searchString} onChange={this.handleChange} placeholder="What are you interested in..." />
<ul onClick={this.handleClick}>
{ libraries.map(function(l){
return <li key={l.name}>{l.name} </li>
})}
</ul>
</div>;
}
});
var libraries = [
{ name: 'Technology', popular: 'true'},
{ name: 'Fishing', popular: 'true'},
{ name: 'School', popular: 'true'},
{ name: 'Camping', popular: 'true'},
];
// Render the SearchExample component on the page
React.render(
<SearchExample items={ libraries } />,
document.getElementById('sidebar')
);
Currently the other JS code is in an html file, but I can change that later.
Pass that function as a prop to SearchExample class
AnotherFile.jsx
var HandleSearch = React.createClass({
handleSearch: function(a) {
//your code here
},
render: function() {
return <SearchExample handleClickOnSearch={this.handleSearch} items={ libraries } />
}
});
search example file
var SearchExample = React.createClass({
getInitialState: function(){
return { searchString: ' ' };
},
handleClick: function(event){
this.props.handleClickOnSearch(this.state.searchString);
},
handleChange: function(e){
this.setState({searchString:e.target.value});
},
render: function() {
var libraries = this.props.items,
searchString = this.state.searchString.trim().toLowerCase();
if(searchString.length > 0){
// We are searching. Filter the results.
libraries = libraries.filter(function(l){
return l.name.toLowerCase().match( searchString );
});
}
else
{
libraries = libraries.filter(function(l){
return l.popular.toLowerCase().match('true');
});
}
return <div>
<input type="text" value={this.state.searchString} onChange={this.handleChange} placeholder="What are you interested in..." />
<ul onClick={this.handleClick}>
{ libraries.map(function(l){
return <li key={l.name}>{l.name} </li>
})}
</ul>
</div>;
}
});
var libraries = [
{ name: 'Technology', popular: 'true'},
{ name: 'Fishing', popular: 'true'},
{ name: 'School', popular: 'true'},
{ name: 'Camping', popular: 'true'},
];
// Render the SearchExample component on the page
React.render(
<HandleSearch />,
document.getElementById('sidebar')
);
I have a select all component rendering in my table header, I would like it to check all of the checkboxes on the page when checked, if one of the checkboxes is unchecked I would like the select all to unselect too:
var SelectAll = React.createClass({
handler: function(e) {
e.target.value;
e.preventDefault();
channel.publish({
channel: "contact",
topic: "selectAll",
data: {
contacts: this.props.data
}
});
},
render: function() {
console.log(this.props.data.children);
var id = this.props.contacts;
var isSelected = this.props.data.isSelected;
return (
<div className="contact-selector">
<input type="checkbox" checked={isSelected}
onChange={this.handler} />
</div>
);
},
});
My checkboxes are rendered in each table row:
var ContactSelector = React.createClass({
getInitialState: function() {
return {
selectedContacts:[]
};
},
handleChange: function(e) {
var id = e.target.attributes['data-ref'].value;
if (e.target.checked === true){
contactChannel.publish({
channel: "contact",
topic: "selectedContact",
data: {
id: id
}});
} else{
contactChannel.publish({
channel: "contact",
topic: "deselectedContact",
data: {
id: id
}});
}
},
render: function() {
var id = this.props.data.id;
var isSelected = this.props.data.IsSelected;
return (
<div className="contact-selector">
<input type="checkbox"
checked={isSelected} data-ref={id}
onClick={this.handleChange} />
</div>
);
}
});
Can anyone please tell me how to setup the check all? Using state or refs etc