Reputation: 695
I have managed to create my function to select a single or multiple boxes. On the single ones I don't have any issue, I mean when you click a box it is checked or unchecked.
But it is not the same case with the select all. Any ideas how can I fix that? (I am using material-ui
library for my boxes, but basically they are simple HTML
input)
Select all component and function:
<Checkbox
name="checkboxes"
checked={this.state.allCheckboxes}
onChange={this.handleAllCheckboxes}
indeterminate
/>Select All
Function:
handleAllCheckboxes = (e) => {
let responseObj = this.state.items;
let prepareObj = {};
if(e.target.checked){
//to do add loop to check all check box
responseObj.forEach(function(item){
if(item.documentId !== null && item.documetNumber !== null ){
prepareObj[item.documentId] = item.documentNumber;
// this.refs.documentId
}
});
let toSee = Object.keys(prepareObj).length > 0 ? true : false;
this.setState({
docList: prepareObj,
visible: toSee
})
let checkboxes = document.getElementsByName('DocCheckbox')
checkboxes.forEach(function(checkbox){
checkbox.checked = checkbox.checked
})
console.log(checkboxes)
} else {
//to do add loop to uncheck all check box
this.setState({
prepareObj: {}
})
}
console.log(prepareObj);
};
Select single component and function:
<Checkbox
name='DocCheckbox'
type='checkbox'
color='default'
value={JSON.stringify({ documentId: rowData.documentId, documentNumber: rowData.documentNumber })}
onClick={this.handleCheckboxClick}/>
Function:
handleCheckboxClick = (e, id) => {
if (id) {
} else {
let parsedVal = JSON.parse(e.target.value);
// console.log(e.target.value)
let newDocList = { ...this.state.docList };
if (e.target.checked) {
this.setState({
singleCheckbox:true
})
newDocList[parsedVal.documentId] = parsedVal.documentNumber;
} else {
delete newDocList[parsedVal.documentId];
this.setState({
singleCheckbox:false
})
}
let toSee = Object.keys(newDocList).length > 0 ? true : false;
this.setState(
{
docList: newDocList,
visible: toSee
},
() => {
console.log(this.state.docList);
}
);
}
};
UPDATE Answer of my code based to the reply of @norbitrial ( The answer is based on my properties , calls and data so feel free to modify it for your purpose )
Step 1 - Create a constructor to maintain your data and global checked state
constructor(props) {
super(props);
this.state = {
docList: {},
checked: false,
hasToCheckAll: false
}
}
Step 2 - Create functions to handle single and multiple checkboxes
Handle single checkbox
handleCheckboxClick = (clickedItem) => {
console.log(clickedItem)
// let parsedVal = JSON.parse(e.target.value);
let newDocList = { ...this.state.docList };
if (!clickedItem.checked) {
newDocList[clickedItem.documentId] = clickedItem.documentNumber;
console.log(newDocList)
} else {
delete newDocList[clickedItem.documentId];
}
let toSee = Object.keys(newDocList).length > 0 ? true : false;
console.log(toSee)
this.setState(
{
docList: newDocList,
visible: toSee
}, ()=>{
console.log(newDocList)
});
const updatedArray = this.state.items.map((item) => {
item.checked = item.documentId === clickedItem.documentId ? !item.checked : item.checked;
return item;
});
this.setState({
items: updatedArray,
});
};
Handle all checkboxes
handleAllCheckboxes = (e) => {
const hasToCheckAll = !this.state.hasToCheckAll;
const updatedArray = this.state.items.map((item) => {
item.checked = hasToCheckAll;
return item;
});
console.log(updatedArray)
let responseObj = this.state.items;
let prepareObj = {};
if (e.target.checked) {
//to do add loop to check all check box
responseObj.forEach(function (item) {
if (item.documentId !== null && item.documetNumber !== null) {
prepareObj[item.documentId] = item.documentNumber;
}
});
let toSee = Object.keys(prepareObj).length > 0 ? true : false;
console.log(toSee)
this.setState({
docList: prepareObj,
// allCheckboxes: true,
items: updatedArray,
hasToCheckAll,
visible: toSee
})
let checkboxes = document.getElementsByName('checkAll')
checkboxes.forEach(function (checkbox) {
checkbox.checked = e.target.checked
})
console.log(checkboxes)
} else {
console.log(updatedArray)
this.setState({
docList: {},
hasToCheckAll:false
})
}
};
Step 3 - Insert the state of the checked boxes inside into the object . And loop over them ( again this is coming from the response from my Back End so for everyone this step will be different )
.then((response) => {
// handle success
let dataItem = response.data.bills;
let prepareDataItem = [];
dataItem.forEach(function (item) {
item.checked = false;
prepareDataItem.push(item);
})
Step 4 - Render the checkboxes (these checkboxes are based on material-ui
library , but it can work for a simple inputs also)
<Checkbox
name='DocCheckBox'
type='checkbox'
checked={rowData.checked}
color='default'
value={JSON.stringify({ documentId: rowData.documentId, documentNumber: rowData.documentNumber })}
onChange={() => this.handleCheckboxClick(rowData)}/>
<Checkbox
name="checkboxes"
checked={this.state.hasToCheckAll}
onChange={this.handleAllCheckboxes}
indeterminate
/>Select All
Upvotes: 4
Views: 4332
Reputation: 15166
I would change a bit how you are handling these things in the application.
Technically you are manipulating the DOM directly, instead of leaving it to React with its states. In this case there is a definitely better way to handle checkbox states in the UI.
The solution:
1. Changed default state:
Let me state that I don't have the real data structure what you have so I have the following in the constructor just for the representation:
constructor(props:any) {
super(props);
this.state = {
items: [
{ documentId: 1, documentNumber: 1234, checked: false },
{ documentId: 2, documentNumber: 1235, checked: false },
{ documentId: 3, documentNumber: 1236, checked: false },
],
hasToCheckAll: false,
}
}
As you can see the items have checked
property in order to handle them in the state.
2. Rendering the checkboxes differently
In the render function I have changed couple of things:
render() {
return (
<div>
<Checkbox
name="checkboxes"
checked={this.state.hasToCheckAll}
onChange={() => this.handleAllCheckboxes()}
indeterminate
/>Select All
{this.state.items.map((item:any) => {
return <div key={item.documentNumber}>
<Checkbox
name="DocCheckbox"
color="default"
checked={item.checked}
value={JSON.stringify({ ...item.documentId, ...item.documentNumber })}
onChange={() => this.handleCheckboxClick(item)}/> {item.documentNumber}
</div>
})}
</div>
)
}
3. Handling checkbox state is also changed:
Take a look at the following handlers:
handleAllCheckboxes = () => {
const hasToCheckAll = !this.state.hasToCheckAll;
const updatedArray = this.state.items.map((item:any) => {
item.checked = hasToCheckAll;
return item;
});
this.setState({
...this.state,
items: updatedArray,
hasToCheckAll: hasToCheckAll,
});
};
handleCheckboxClick = (clickedItem:any) => {
const updatedArray = this.state.items.map((item:any) => {
item.checked = item.documentId === clickedItem.documentId ? !item.checked : item.checked;
return item;
});
this.setState({
...this.state,
items: updatedArray,
});
};
The result:
Of course you can extend this example with your data manipulation if there is a further need. The solution has been built with TypeScript
- I had a project opened with that - but you can remove types where it has been added.
The above example works like charm, I hope this helps!
Upvotes: 1