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 ?