so i was searching for a quick plugin to show nested checkbox for my react project and unfortunately most of what iv'e found wasn't as i was expecting, at-least for my needs.
i wanted to create a checkbox with network name and its has instances as children checkboxes , if u click on the network it will work as a toggle button to select all instances within this network, while you still have the option to select instances individually and if all instances were selected change the network to checked u know the usual toggle behaviour.
so i decide to write a quick one and thought it might be handy for some of you guy.
ill show 2 nested checkbox here one using react with redux and the 2nd will use a react-admin component
first ill create some dummy data:-
this is an array of objects that have network and instances
const networks = [{
name: "Network_A",
id: 1,
instances: [{
id: 0,
instanceName: "Instance_1"
},
{
id: 1,
instanceName: "Instance_2"
}
]
},
{
name: "Network_B",
id: 33,
instances: [{
id: 0,
instanceName: "Instance_1",
},
{
id: 1,
instanceName: "Instance_2",
},
{
id: 2,
instanceName: "Instance_3",
}
]
}
]
ok cool now what ?
lets write our class and call it CheckboxesGroup
the class will use some ready controllers from material-ui
like this
import React from 'react'
import FormLabel from '@material-ui/core/FormLabel'
import FormControl from '@material-ui/core/FormControl'
import FormGroup from '@material-ui/core/FormGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import FormHelperText from '@material-ui/core/FormHelperText'
import Checkbox from '@material-ui/core/Checkbox'
import {PropTypes} from 'prop-types'
import { Field } from 'redux-form'
class CheckboxesGroup extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
instances: PropTypes.array.isRequired
}
constructor(props) {
super(props)
this.classes = {
root: {
display: 'flex'
},
formControl: {
margin: '3px',
float: 'left'
}
}
const networkName = props.name
const instances = props.instances.map(item => {
return {name: item.instanceName, value: false}
})
this.onChange=props.onChange
this.state = {
networkName,
checkedAll: false,
instances
}
this.handleChange.bind(this)
}
render() {
const {checkedAll} = this.state
const checkboxes = this.state.instances.map(i => {
const instanceName=i.name
return (
<FormControlLabel
style={{width: '200px'}}
control={
<Field name={`${instanceName}`} type="checkbox" component={renderInnerCheckboxField} label="instances" checked={checkedAll || i.value} onChange={this.handleChange(i.name)} value={i.value.toString()}/>
}
label={i.name}
key={i.name + i.value}
> </FormControlLabel>
)
})
const networkName=this.state.networkName
return (
<div className={this.classes.root.toString()}>
<br />
<FormControl
component="fieldset"
className={this.classes.formControl.toString()}
>
<FormLabel
component="legend"
style={{fontWeight: 'bold', fontSize: '20px'}}
>
{this.state.networkName}
<FormControlLabel
label="Select All"
control={
<div>
<Field name={`network ${networkName}`} type="checkbox" checkboxes={checkboxes} component={renderCheckboxField} label="Sellect all in" checked={checkedAll} onChange={event => {
this.setState({
checkedAll: event.target.checked
})
}}/>
</div>
}
/>
</FormLabel>
<FormGroup style={{display: 'flow-root'}}>
{checkboxes}
</FormGroup>
<FormHelperText>
--------------------------------------------------------------------------------
</FormHelperText>
</FormControl>
</div>
)
}
handleChange(name) {
const _this = this
return function(event) {
const instances = _this.state.instances.map(i => {
if (i.name === name) {
console.log(event.target.checked)
return {name, value: event.target.checked}
}
return i
})
_this.setState({
..._this.state,
instances
})
setTimeout(
() => {
_this.onChange(_this.state)
},
500
)
}
}
}
const renderCheckboxField = (props) => {
const { input, label, meta} = props
console.log("...custom ",props)
return (
<Checkbox
label={label}
{...input}
/>
)}
const renderInnerCheckboxField = ({ input, label, meta: { touched, error }, ...custom }) => {
return (
<Checkbox
label={label}
error={!!(touched && error)}
helperText={touched && error}
{...input}
{...custom}
/>
)}
export default CheckboxesGroup
now you can call it inside you form or any render component
in my case i put it inside a FormTab like this
<FormTab label="Networks & Instances">
{networks.map(network =>
(<CheckboxesGroup {...network} source="networks" key={network.name} />)
)}
</FormTab>
but after this one i realised that i was complexing things up so , idid a quicker one with simple components from react admin
and here is the one for react-admin
import React from 'react'
import {CheckboxGroupInput} from 'react-admin'i
import {FormSpy , useForm} from 'react-final-form'
import {BooleanInput} from 'react-admin'
const Instance = ({record}) => {
return (
<div key={record.instanceName} className="instances">
{record.instanceName + ' - ' + record.name}
</div>
)
}
const SelectAllBtn = props => {
const {network} = props
const form = useForm()
return (
<BooleanInput
label={network.name}
key={network.id}
source={`network.n_${network.id}`}
onChange={e => {
let instances = []
if (e) {
instances = network.instances.map(i => i.id)
}
form.change('networks.n_' + network.id, instances)
}}
/>
)
}
export const NetworkInstances = ({gameNetworks}) => {
if (gameNetworks) {
return gameNetworks.map(network => {
if (network.instances.length > 1) {
return (
<div key={network.name}>
<FormSpy>
{props => {
return (
<SelectAllBtn
network={network}
form={props}
/>
)
}}
</FormSpy>
<CheckboxGroupInput
source={`networks.n_${network.id}`}
choices={network.instances}
optionText={<Instance />}
key={network.id}
id={`n_${network.id}`}
label={network.name}
/>
</div>
)
}
return (
<CheckboxGroupInput
source={`networks.n_${network.id}`}
choices={network.instances}
optionText={<Instance />}
key={network.id}
id={`n_${network.id}`}
label={network.name}
/>
)
})
}
return <div />
}
and just like the previous one you can use it directly in the render
function with passing the source or the api call
<FormTab label="Networks & Instances" path="networks">
<NetworkInstances
gameNetworks={networks}
/>
</FormTab>
if you have any questions feel free to ask me
Top comments (1)
Can you share a working snippet of the same ?