I am using react checkbox tree package. I have a treeview with checkbox as below.
const nodes = [
{
value: "mars",
label: "Mars",
children: [
{
value: "phobos",
label: "Phobos"
},
{ value: "deimos", label: "Deimos" }
]
},
{
value: "saturn",
label: "Satrun"
},
{
value: "jupitor",
label: "Jupitor"
}
];
function Widget() {
const [checked, setChecked] = useState([]);
const [expanded, setExpanded] = useState([]);
const updateCheckedState = (node) => {
const childValues = [];
const isParent = node.isParent;
const updatedValues = isParent ? childValues : [node.value];
if (node.checked) {
setChecked([...updatedValues]);
} else {
const filteredChecks = checked.filter((check) => {
return !updatedValues.includes(check);
});
setChecked(filteredChecks);
}
};
return (
<CheckboxTree
iconsClass="fa5"
nodes={nodes}
checked={checked}
expanded={expanded}
onCheck={(nodes, node) => {
updateCheckedState(node);
}}
onExpand={(expanded) => setExpanded(expanded)}
/>
);
}
Full example is here
My issue is when I clicked checkbox with children it doesn't checked(example Mars). But I clicked no children element then it checked. Please help me to fix this.
checked is the array of node values, so you will need to assign the children's values.
const updatedValues = isParent
? node.children.map((v) => v.value)
: [node.value];
Related
I have multiselect dropdown items with checkboxes in my react app, what I am trying to achieve is if I checked any three items, then dropdown should display maximum items selected and if I unchecked anyone of them, it should display back the drop down box with items.
Somehow it doesn't work, could someone please advise.
CodeSanbox link
https://codesandbox.io/s/musing-sun-swvj6y?file=/src/App.js
import { useState } from "react";
import Multiselect from "multiselect-react-dropdown";
import "./styles.css";
export default function App() {
const options = [
{ key: "Apple", email: "apple#test.com", id: 1 },
{ key: "Orange", email: "oranges#test.com", id: 2 },
{ key: "Mango", email: "mango#test.com", id: 3 },
{ key: "Grapes", email: "grapes#test.com", id: 4 }
];
const [option, setOption] = useState([]);
const [selectedOption, setSelectedOption] = useState([]);
const [maxOptions, setMaxOptions] = useState(0);
const handleTypeSelect = (e, i) => {
const copy = [...selectedOption];
copy.push(e[3 - maxOptions]);
setSelectedOption(copy);
setMaxOptions((prevState) => prevState - 1);
};
const handleTypeRemove = (e) => {
const copy = [...selectedOption];
let index = copy.indexOf(e);
copy.splice(index, 1);
setSelectedOption(copy);
setMaxOptions((prevState) => prevState + 1);
};
options.forEach((option) => {
option.displayValue = option.key + "\t" + option.email;
});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Multiselect
onSelect={(e) => handleTypeSelect(e, selectedOption.length)}
onRemove={handleTypeRemove}
options={selectedOption.length + 1 === maxOptions ? [] : options}
// options={!showOptions ? [] : option}
displayValue="displayValue"
showCheckbox={true}
emptyRecordMsg={"Maximum fruits selected !"}
/>
</div>
);
}
The library doesn't support manually selecting/deselecting the options. Reference
There is one hack that you can do. You can play around with key. Use the selectedItems as key and then it will re-mount the component whenever the selectedItems changes.
Note that this hack is not the recommended way to do "React".
You update the options based on the size of the selected options
const maxSelectableItems = 3;
const options = [
{ key: "Apple", email: "apple#test.com", id: 1 },
{ key: "Orange", email: "oranges#test.com", id: 2 },
{ key: "Mango", email: "mango#test.com", id: 3 },
{ key: "Grapes", email: "grapes#test.com", id: 4 }
];
<Multiselect
// This will re-mount your component whenever
// selected items changes
key={selectedItems}
onSelect={handleSelection}
onRemove={handleRemove}
// This will set the pre-selected values on re-mount
selectedValues={selectedItems}
options={
selectedItems.length === maxSelectableItems
? []
: options.map((o) => ({
...o,
displayValue: `${o.key}\t${o.email}`
}))
}
displayValue="displayValue"
showCheckbox
emptyRecordMsg={"Maximum fruits selected !"}
/>
And, your handleSelection and handleRemove will look like this:
const [selectedItems, setSelectedItems] = useState([]);
const handleSelection = (selectedItems) => {
setSelectedItems(selectedItems.slice(0, maxSelectableItems));
};
const handleRemove = (selectedItems) => {
setSelectedItems(selectedItems);
};
One issue with this is that since it re-mounts the entire multi-select component, when you select/remove an item, the input will lose focus. So, you will have to manually give focus to the input after selection/removal.
const focusOnInput = () => {
setTimeout(() => {
// You can use a better selector (this is just a generic input selector)
document.querySelector("input").focus();
// Adding some delay to allow the component to re-mount
}, 10);
};
And then, use this in your selection/removal handlers
const handleSelection = (selectedItems) => {
setSelectedItems(selectedItems.slice(0, maxSelectableItems));
focusOnInput()
};
const handleRemove = (selectedItems) => {
setSelectedItems(selectedItems);
focusOnInput()
};
Here is a link to a working sandbox
import { useState, useRef } from "react";
import Multiselect from "multiselect-react-dropdown";
import "./styles.css";
export default function App() {
const options = [
{ key: "Apple", email: "apple#test.com", id: 1 },
{ key: "Orange", email: "oranges#test.com", id: 2 },
{ key: "Mango", email: "mango#test.com", id: 3 },
{ key: "Grapes", email: "grapes#test.com", id: 4 }
];
const [option, setOption] = useState([]);
const [selectedOption, setSelectedOption] = useState([]);
const fruitmultiselect = useRef(null);
options.forEach((option) => {
option.displayValue = option.key + "\t" + option.email;
});
return (
<div className="App">
<h1>Hello CodeSandbox3</h1>
<Multiselect
ref={fruitmultiselect}
onSelect={(selectedList, selectedItem) => {
if(selectedList.length === 3){
fruitmultiselect.current.toggelOptionList()
console.log("onSelect length: "+selectedList.length);
}
}}
options={options}
displayValue="displayValue"
showCheckbox={true}
selectionLimit={3}
closeOnSelect={true}
emptyRecordMsg={"Maximum fruits selected !"}
/>
</div>
);
}
I have a form.
This returns back and loads its value which is correct,
however when I start typing it only returns its value plus one letter from the onChange.
here is what I mean
How do I clear the value of the form when the onChange is executed? I have tried clearing the form using config.setStateVar('') which still does not seem to work. Does anyone have any ideas?
const onChange = (e, config) => {
config.setStateVar('')
const value = e.target.innertext ? e.target.innertext
: e.target.value
config.setStateVar(value)
}
Full Code:
const editCriticalObjects = props => {
const query = new URLSearchParams(props.location.search)
const criticalObjectsId = query.get('id')
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [editCriticalObjects, setEditCriticalObjects] = useState([])
const [criticalObjectTitle, setCriticalObjectTitle] = useState('')
const [criticalObjectDomainName, setCriticalObjectDomainName] = useState('')
const [criticalObjectType, setCriticalObjectType] = useState('')
const [findVal, setFindVal] = useState('')
useEffect(() => {
async function onLoadCriticalObjects() {
loading.setLoading(true)
const results = await get(`get_critical_objects?id=${criticalObjectsId}`, user.user)
setEditCriticalObjects(results)
loading.setLoading(false)
}
onLoadCriticalObjects()
}, [])
const assetIdObject = editCriticalObjects.filter(obj => {
if (obj.asset_id === criticalObjectsId) {
return obj
}
return assetIdObject
})
const configs = [
{
label: 'Title',
name: 'Title',
value: assetIdObject.map(item => item.asset_id),
field: SinglelineTextfield,
uniqueIdentifier: 0,
stateVar: criticalObjectTitle,
setStateVar: setCriticalObjectTitle,
},
{
name: 'type',
uniqueIdentifier: 1,
label: 'type',
value: assetIdObject.map(item => item.type),
field: SinglelineTextfield,
stateVar: criticalObjectType,
setStateVar: setCriticalObjectType,
},
{
name: 'Domain',
label: 'Domain',
value: assetIdObject.map(item => item.Domains),
field: SinglelineTextfield,
uniqueIdentifier: 2,
stateVar: criticalObjectDomainName,
setStateVar: setCriticalObjectDomainName,
}
]
const criticalObjectParams = {
asset_ids: criticalObjectTitle,
asset: true,
merge: true,
}
console.log(criticalObjectParams)
const onChange = (e, config) => {
const value = e.target.innertext ? e.target.innertext
: e.target.value
config.setStateVar(value)
}
const onSubmitClick = async () => {
loading.setLoading(true)
await verifiedPost('post_critical_objects', criticalObjectParams, user.user)
loading.setLoading(false)
}
return (!editCriticalObjects ? null :
<PageWrapper title={`Edit Critical Object: ${criticalObjectsId}`}>
<div className='Main Card EditCriticalObjectFormWrapper'>
{configs.map((config, k)=> {
const Field = config.field
return (
<React.Fragment key={k}>
<Field
style={{ marginBottom: '15px'}}
name={config.name}
placeholder={config.name}
value={config.value}
initialValue={config.value}
initialValues={config.value}
uniqueIdentifier={k}
label={config.label}
onChange={(e) => onChange(e, config)}
/>
</React.Fragment>
)
})}
<Button
className='Button Dark Main'
text='Submit'
onClick={onSubmitClick}
/>
</div>
</PageWrapper>
)
}
export default editCriticalObjects
I have an object
const configs = [
{
label: 'Title',
name: 'Title',
value: titleToString,
field: SinglelineTextfield,
uniqueIdentifier: 0,
stateVar: 'criticalObjectTitle',
setStateVar: 'setCriticalObjectTitle',
},
{
name: 'type',
uniqueIdentifier: 1,
label: 'type',
value: typeToString,
field: SinglelineTextfield,
stateVar: 'criticalObjectType',
setStateVar: 'setCriticalObjectType',
},
{
name: 'Domain',
label: 'Domain',
value: domainToString,
field: SinglelineTextfield,
uniqueIdentifier: 2,
stateVar: 'criticalObjectDomainName',
setStateVar: 'setCriticalObjectDomainName',
}
]
I am retrieving the setState Variable wwwith the setFindVal in my onChange
const onChange = async (e) => {
const name = e.target.name || e.target.getAttribute('name')
const value = e.target.innertext ? e.target.innertext
: e.target.value
const setFindVal = configs.find(config => config.name === name).setStateVar
setFindVal(value)
console.log(setFindVal(value));
}
However, when I want to use setFindVal as a useState hook, it tells me that setFindVal is not defined..When I log setFindVal and its property type i get its a string, so I have created a useState hook outside of my function, however this still does not work. How do I convert a string into a useState function?
Error:
Uncaught (in promise) TypeError: setFindVal is not a function
Full code:
const editCriticalObjects = props => {
const query = new URLSearchParams(props.location.search)
const criticalObjectsId = query.get('id')
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const testing = useContext(TestingFrameworkContext)
const [editCriticalObjects, setEditCriticalObjects] = useState(null)
const [findval, setFindVal] = useState(null)
const [criticalObjectTitle, setCriticalObjectTitle] = useState(null)
const [criticalObjectDomainName, setCriticalObjectDomainName] = useState(null)
useEffect(() => {
async function onLoadCriticalObjects() {
loading.setLoading(true)
const results = await get(`get_critical_objects?id=${criticalObjectsId}`, user.user)
setEditCriticalObjects(results)
loading.setLoading(false)
}
onLoadCriticalObjects()
}, [])
const assetIdObject = editCriticalObjects && editCriticalObjects.filter(obj => {
if (obj.asset_id === criticalObjectsId )
return obj
})
console.log(assetIdObject);
const title = assetIdObject && assetIdObject.map(item => item.asset_id)
console.log(titleToString);
const titleToString = title && title.join(', ')
const domain = assetIdObject && assetIdObject.map(item => item.Domains.toString())
const domainToString = domain && domain.join(', ')
const type = assetIdObject && assetIdObject.map(item => item.type.toString())
const typeToString = type && type.join(', ')
const configs = [
{
label: 'Title',
name: 'Title',
value: titleToString,
field: SinglelineTextfield,
uniqueIdentifier: 0,
stateVar: 'criticalObjectTitle',
setStateVar: 'setCriticalObjectTitle',
},
{
name: 'type',
uniqueIdentifier: 1,
label: 'type',
value: typeToString,
field: SinglelineTextfield,
stateVar: 'criticalObjectType',
setStateVar: 'setCriticalObjectType',
},
{
name: 'Domain',
label: 'Domain',
value: domainToString,
field: SinglelineTextfield,
uniqueIdentifier: 2,
stateVar: 'criticalObjectDomainName',
setStateVar: 'setCriticalObjectDomainName',
}
]
const getObjectParams = {
asset_ids: criticalObjectTitle,
asset: true,
merge: true,
}
const onChange = async (e) => {
const name = e.target.name || e.target.getAttribute('name')
const value = e.target.innertext ? e.target.innertext
: e.target.value
const setFindVal = configs.find(config => config.name === name).setStateVar
setFindVal(value)
console.log(setFindVal(value));
}
const onClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_critical_objects', testing.getObjectParams, user.user)
loading.setLoading(false)
console.log(getObjectParams);
}
return (!editCriticalObjects ? null :
<React.Fragment>
<PageWrapper title='Edit Critical Objects:'>
<div className='Main Card editCriticalObjectFormWrapper'>
{configs.map((config, k)=> {
const Field = config.field
return (
<React.Fragment key={config.name} >
<Field
style={{ marginBottom: '15px'}}
name={config.name}
placeholder={config.name}
value={config.stateVar}
initialValue={config.stateVar}
initialValues={config.stateVar}
uniqueIdentifier={k}
label={config.label}
onChange={onChange}
/>
</React.Fragment>
)
})}
<Button
className='Button Ghost Approve'
text='Submit'
onClick={onClick}
/>
</div>
</PageWrapper>
</React.Fragment>
)
}
export default editCriticalObjects
logging one value
new onchange:
const onChange = async (e) => {
const name = e.target.name || e.target.getAttribute('name')
const value = e.target.innertext ? e.target.innertext
: e.target.value
const setFindVal = configs.find(config => config.name === name).setStateVar;
setStateValues[setFindVal](value);
console.log(setFindVal, value);
}
The reason your getting it's not a function is because you are just using the value from the configs object which is a string.
You should assign the function as the value in config
const configs = [
{
...
setStateVar: setCriticalObjectTitle,
},
{
...
setStateVar: setCriticalObjectType,
},
{
...
setStateVar: setCriticalObjectDomainName,
}
];
How to make field validation?
I have an object with fields from which I generate a form, and when submitting, I need to check each field so that it is not empty, I do this, but it doesn’t work
My form:
const [volunteerData, setVolunteerData] = useState({
fullName: {
value: '',
type: "text",
placeholder: "Name",
label: "Name"
},
phone: {
value: '',
type: "text",
placeholder: "Phone number",
label: "Phone number",
mask: "+7(999) 999 99 99"
}
)}
Render form:
const onHandleRenderForm = () => {
return Object.keys(volunteerData).map((items, idx) => {
const control = volunteerData[items];
return (
<div key={idx} className="content-data-box">
<label>{control.label}</label>
<InputMask
type={control.type}
placeholder={control.placeholder}
mask={control.mask}
onChange={e => onHandleFormData(e, items)}
/>
</div>
)
})
};
onChange input:
const onHandleFormData = (e, items) => {
const before = {...volunteerData};
const after = {...before[items]}
after.value = e.target.value;
before[items] = after;
setVolunteerData(before);
};
onClick (submit button):
const onHandleErrorBoundary = () => {
Object.keys(volunteerData).map(items => {
const errorData = items.value === '';
console.log(errorData)
})
};
Change items.value === '' to volunteerData[items].value !== ""
const onHandleErrorBoundary = () => {
Object.keys(volunteerData).map(items => {
const errorData = volunteerData[items].value !== "";
return console.log(errorData);
});
};
you can check here codesandbox
so I worked on a project using react js with umi js and antd as additional dependencies,
I had a problem when I got the task to disable each row in the antd table,
I tried to read the documentation antd but got nothing,
is it possible that you can do that? or there is another possible way to doing that
Thank you for the help
here's my code :
/* eslint-disable */
import React, { useState, useEffect, useRef } from 'react';
import { Modal, Button, Select, message, Radio, Table, Alert } from 'antd';
import _ from 'lodash';
import axios from 'axios';
import cookies from 'js-cookie';
import {_getCurrentBusiness} from '../../../utils/utils_business';
import {formatMessage } from 'umi-plugin-locale';
function DeleteConfirm (props) {
const user_auth = cookies.getJSON('ckmsbp');
const business = _getCurrentBusiness();
const [radio, setRadio] = useState('all');
const [role, setRole] = useState(null);
const [chRole, setChrole] = useState(null); //changerole
const [btn, setBtn] = useState(false);
const isMounted = useRef(null);
const roleRef = useRef(null);
const spanAmount = {fontSize: '1rem', fontWeight: 500,marginLeft: '1rem'}
useEffect( () => {
isMounted.current = true;
return () => isMounted.current = false;
}, [])
useEffect( () => {
if(!_.isNil(props.roles)) {
const updateRole = _.filter(props.roles, r => !_.eq(r.id, props.role.id) );
setRole(updateRole); //tampil di select
}
}, [props.roles]);
const handleSubmit = async () => {
let accountMeta = {}
const body = {status: 'deleted'}
const params = { _id: props.role.id}
console.log('radio', radio);
if(_.eq(radio, 'all')){
if(_.isNil(chRole)) {
message.error('data can not empty')
props.chVisible(null); //close modal
}
_.forEach(props.account, async acc => {
let bus = [];
if( !_.isNil(acc.business) && _.isString(acc.business) ) bus = JSON.parse(acc.business);
const find = _.findIndex(bus, b => {
return _.eq(b._id, business._id) && _.eq(b.role_capability, props.role.id)
})
bus[find].role_capability = chRole;
acc.business = JSON.stringify(bus)
accountMeta = {
value : acc.business,
key : 'business',
account_id: acc._id
}
await axios.put(`${API}/account-meta`, accountMeta, { headers: { Authorization: user_auth.token}});
})
} else if( _.eq(radio, 'manual')){
console.log('asd');
} else if (_.eq(radio, 'delete')){
_.forEach(props.account, async acc => {
let bus = [];
if( !_.isNil(acc.business) && _.isString(acc.business) ) bus = JSON.parse(acc.business);
const find = _.findIndex(bus, b => _.eq(b._id, business._id) && _.eq(b.role_capability, props.role.id) )
if(_.gt(find, -1)){
acc.business = JSON.stringify([])
accountMeta = {
value : acc.business,
key : 'business',
account_id: acc._id
}
await axios.put(`${API}/account-meta`, accountMeta, { headers: { Authorization: user_auth.token}});
}
})
}
const deleteResult = await axios.put(`${API}/master`, body, { params, headers: { Authorization: user_auth.token}});
if(!_.isNil(deleteResult) && _.isObject(deleteResult.data)){
let no = 1;
let data = []
let updateRole = _.filter(props.roles, r => !_.eq(r.id, props.role.id));
_.map(updateRole, role => {
role.no = no;
data.push(role)
no++
});
props.data(data); //tampil di select
message.success('data updated!')
props.chVisible(null); //close modal
}
}
const onChange = (data) => {
const value = data.target.value
setRadio(value);
}
const roleChange = (data) => {
setChrole(data)
}
//props column diambil dari datasource
const columns = [
{
title : 'No',
dataIndex: 'no',
key : 'no',
},
{
title : 'Account\'s Name',
dataIndex: 'name',
key : 'name',
},
{
title : 'Change Role',
dataIndex: 'id',
key : 'action',
render : (text, data) => renderButton(text, data)
},
];
const handleClick = (e, data) => {
setBtn(!btn)
console.log('e', e);
console.log('data', data);
}
const rowClassName = (record, index) => {
console.log('record', record);
console.log('index',index);
}
const renderButton = () => {
let arrayAllow = [];
arrayAllow.push(
<Select
showSearch
key = '1'
size = "small"
placeholder = "select"
ref = {roleRef}
optionFilterProp = "children"
style = {{ width: 100 }}
onChange = {(e) => roleChange(e)} //handle change role
filterOption = {(input, option) => _.toLower(option.props.children).indexOf(_.toLower(input)) >= 0}
>
{
!_.isNil(role) && _.map(role, (newVal) => {
return (<Select.Option
key = {newVal.id}
title = {newVal.title}
value = {newVal.id}>{newVal.title}
</Select.Option>)
})
}
</Select>
)
arrayAllow.push( <Button
type = {!btn ? "danger" : "primary"}
key = '2'
icon = {!btn ? "close" : "redo"}
size = "small"
onClick = {(e) => handleClick(e, props.role.id)}
/> )
return arrayAllow
}
// R E N D E R I N G
return(
<div>
<Modal
title = {`${formatMessage({id: 'ROLE_MANAGEMENT.DELETE_CONFIRM_TITLE'})} ${props.role.title}`}
visible = {props.visible}
onOk = {() => handleSubmit()}
onCancel = {() => props.cancel(null) }
width = {800}
>
<p>{formatMessage({id : 'ROLE_MANAGEMENT.DELETE_CONFIRM_STATEMENT', title: props.role.title})}</p>
<div style={{marginBottom: '1rem'}}>
<Radio.Group onChange = {(e) => onChange(e)} value={radio}>
<Radio value="all" >Changed All of people </Radio>
<Radio value="manual">Changed people manually</Radio>
<Radio value="delete">Total delete </Radio>
</Radio.Group>
</div>
{ _.eq(radio, 'all') &&
<div>
<Select
showSearch
ref = {roleRef}
size = "large"
style = {{ width: 200 }}
placeholder = {formatMessage({id: 'ACCOUNT.PLCHOLDER_ROLE'})}
optionFilterProp = "children"
onChange = {(e) => roleChange(e)} //handle change role
filterOption = {(input, option) => _.toLower(option.props.children).indexOf(_.toLower(input)) >= 0}
>
{
!_.isNil(role) && _.map(role, (newVal) => {
return ( <Select.Option
key = {newVal.id}
title = {newVal.title}
value = {newVal.id}
>{newVal.title}
</Select.Option> )
})
}
</Select>
<span style={spanAmount}>{`Total amount of people which have role ${props.role.title} : ${_.size(props.account)}`}</span>
</div>
}
{ _.eq(radio, 'manual') && <Table
dataSource = {props.account}
rowClassName = {rowClassName}
columns = {columns}
pagination = {{ pageSize: 50 }}
scroll = {{ y: 250 }}
/>
}
{ _.eq(radio, 'delete') && <Alert
message = "Attention!"
description = {formatMessage({id: 'ROLE_MANAGEMENT.DELETE_CONFIRM_DELETE'})}
type = "warning"
showIcon
/>
}
</Modal>
</div>
)
}
export default DeleteConfirm;
*the picture that I intend to disable when clicked on the danger button
In Antd there is no simple way to disable a row, so you can do it as workaround like below
So basically when you click on close button you can have state whether its been enabled or disabled as a boolean value
so each record will have that key. so based on that you can add a className and style it as disabled.
Here is a sample code snippet
App.js
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import { Table } from "antd";
import "./styles.css";
function App() {
const dataSource = [
{
key: "1",
name: "Mike",
age: 32,
address: "10 Downing Street",
enabled: true
},
{
key: "2",
name: "John",
age: 42,
address: "10 Downing Street",
enabled: false
}
];
const columns = [
{
title: "Name",
dataIndex: "name",
key: "name"
},
{
title: "Age",
dataIndex: "age",
key: "age"
},
{
title: "Address",
dataIndex: "address",
key: "address"
}
];
return (
<>
<Table
dataSource={dataSource}
columns={columns}
rowClassName={record => !record.enabled && "disabled-row"}
/>
;
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
style.css
.disabled-row {
background-color: #dcdcdc;
pointer-events: none;
}
I hope this way you will have better understanding of solving the problem
Working codesandbox