I dynamically create checkboxes in item forms, when I submit a form in item forms, I get an array of checkbox IDs. How can I either bind true / false to these IDs or overwrite the object, create a new one or something else by clicking on the checkbox? How to understand which checkbox was clicked and bind a value to it true / false?
const [checkboxData, setCheckboxData] = useState(undefined);
useEffect(() => {
(async () => {
const data = await request(`/document-type/typeDocuments`, { method: 'get', cancelTokenKey: 'typeDocuments' });
if (data) { setCheckboxData(data.content); }
})();
}, []);
function onChangeCheckbox(checkedValues) {
setCheckboxData(prevState => {
prevState.map(item => ...)
}) // tried this, but then when you click on the checkbox everything breaks
}
<Form
onFinish={onFormFinish}
onFinishFailed={onFormFailed}
>
<Form.Item
name="typesDocuments"
valuePropName="checked"
>
<Checkbox.Group style={{ width: '100%' }}>
<Row>
{checkboxData?.map(item => <Col span={12}><Checkbox key={item?.id} value={item?.id} onChange={onChangeCheckbox}>{item?.Name}</Checkbox></Col>)}
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item>
<Space style={{ display: 'flex', justifyContent: 'end' }}>
<Button type="primary" htmlType="submit"> {intl("modal-btn-ok")} </Button>
</Space>
</Form.Item>
</Form>
For ease of viewing, I created an example of a project in a
sandbox
Related
I need to make my current draggable component's dropdown to not render on specific cell (specifically the first Row). However, it will still show the cell just without the dropdown. I tried to search for a way however could not find any. Appreciate any help / directions given. Thanks! I also also attached the screenshot of current and expected. (Just take note of the dropdown being empty)
*Current Interface - all dropdown rendered
*Expected New Interface - first dropdown not rendered
Main code with ant design component Row/Col and Select/Option for the dropdown:
render() {
return (
<div>
<Table
scroll={{ x: 300 }}
pagination={{
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '25', '50'],
}}
columns={this.state.columns}
dataSource={this.state.datas}
></Table>
<Modal
destroyOnClose={true}
width={'80%'}
title='Approval Sequence'
visible={this.state.isVisible}
onOk={() => {
updateFlowType(this.state.currentItem.id, {
name: this.state.currentItem.name,
sites: this.state.initTags.map((x) => ({
idSite: x.id,
returnToStep: x.returnToStep,
})),
})
.then((r) => {
notification['sucess']({
message: 'Success',
description: 'Save data success',
});
this.setState({ isVisible: false });
this.getData();
})
.catch(() => {
notification['failed']({
message: 'Failed',
description: 'Failed to save',
});
});
}}
onCancel={() => {
this.setState({ isVisible: false });
}}
>
<Row gutter={[2, 2]}>
<Col style={{ textAlign: 'center' }} span={8}>
<Text strong>Role</Text>
</Col>
<Col style={{ textAlign: 'center' }} span={8}>
<Text strong> Return to Department: </Text>
</Col>
</Row>
<div className='list-area'>
<div className='drag-list'>
<DraggableArea
onChange={(initTags) => this.setState({ initTags })}
isList
tags={this.state.initTags}
render={({ tag }) => (
<Row>
<Col span={8}>
<Button style={{ width: '100%' }}>{tag.name}</Button>
</Col>
<Col span={16}>
<Select
onChange={(e) => {
//create clone
let clone = [...this.state.initTags];
let item = clone.find((x) => x.id === tag.id);
let itemReject = clone.find((x) => x.name === e);
console.log('itemReject', itemReject);
//create returnToStep in item
item.returnToStep = itemReject.id;
this.setState({
initTags: clone,
});
}}
//placeholder = 'Select return step'
style={{ width: '100%' }}
>
{this.getReject(tag.name).map((newTag) => (
//getReject function will slice to get only items before the current iteration object (e.g. if current is index 3, only get index 0~2)
<Option key={newTag.name} value={newTag.name}>
{newTag.name}
</Option>
))}
</Select>
</Col>
</Row>
)}
></DraggableArea>
</div>
</div>
</Modal>
</div>
);
}
You can do "conditional render" using a condition and a component like this:
const someBoolean = true;
retrun (
{ someBoolean && <SomeComponent /> }
)
if someBoolean is true, then <SomeComponent/> will show.
if someBoolean is false, then <SomeComponent/> will not show.
So, just use that to conditionally render your dropdown column or any other component.
If, the table rows are based upon content and dynamically rendered, then I would modify the content accordingly. (i.e. don't provide the rows you don't want to render).
I want to set all TextField to the initial state after adding data to the table by clicking ADD button
I am able to set that to the initial state by using AssignSearchesForm.resetForm(); but what happens here if I set this in add button click it will reset data but my table data also get removed because it's updated Formik values setTeamData([values, ...teamdata]);
I am adding Formik value in this state after onClick it will get added but if I reset form and set initial this state value also getting set empty so what I want after adding data into table TextField get reset or initial but table data will stay the same either if we do not update that
This way I am trying
<Button
onClick={() => {
AssignSearchesForm.handleSubmit();
// I tried this two way
//first way
// AssignSearchesForm.resetForm();
//second way
// AssignSearchesForm.setFieldValue("selectName","")
// AssignSearchesForm.setFieldValue("selectAge","")
// AssignSearchesForm.setFieldValue("location","")
}}
variant="contained"
>
Add
</Button>
export default function App() {
const [teamdata, setTeamData] = React.useState([]);
const AssignSearchesForm = useFormik({
initialValues: {
selectName: "",
selectAge: "",
location: ""
},
validationSchema,
onSubmit: (values) => {
setTeamData([values, ...teamdata]);
}
});
let filteredArray = nameList.filter(
(e) => !teamdata.some((data) => data.selectName === e.selectName)
);
const handleChange = (e) => {
const selectedName = e.target.value;
const name = nameList.find((data) => data.selectName === selectedName);
const newOptions = Object.values(name).reduce((optionList, key) => {
optionList.push({ value: key, label: key });
return optionList;
}, []);
AssignSearchesForm.setFieldValue("selectName", selectedName);
AssignSearchesForm.setFieldValue("selectAge", newOptions[1]?.value || "");
AssignSearchesForm.setFieldValue("location", newOptions[2]?.value || "");
};
return (
<div className="App">
<Grid container direction="row" spacing={1}>
<Grid item xs={4}>
<TextField
sx={{ minWidth: 150 }}
select
id="outlined-basic"
label="Select Name"
name="selectName"
size="small"
onChange={handleChange}
value={AssignSearchesForm.values.selectName}
error={
AssignSearchesForm.errors.selectName &&
AssignSearchesForm.touched.selectName
}
>
{filteredArray?.map((option) => (
<MenuItem key={option.selectName} value={option.selectName}>
{option.selectName}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={4}>
<TextField
id="outlined-basic"
label="location"
name="location"
size="small"
{...AssignSearchesForm.getFieldProps("location")}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="outlined-basic"
label="Select Age"
name="selectAge"
size="small"
{...AssignSearchesForm.getFieldProps("selectAge")}
/>
</Grid>
<Grid item xs={4}>
<Button
onClick={() => {
AssignSearchesForm.handleSubmit();
// AssignSearchesForm.resetForm();
// AssignSearchesForm.setFieldValue("selectName","")
// AssignSearchesForm.setFieldValue("selectAge","")
// AssignSearchesForm.setFieldValue("location","")
}}
variant="contained"
>
Add
</Button>
</Grid>
</Grid>
<Table teamdata={teamdata} />
</div>
);
}
You can leverage the second parameter formikHelper in onSubmit function of formik.
Better approach pointed out by dev-developer in comments:
onSubmit: (values, formikHelper) => {
setTeamData([values, ...teamdata]);
formikHelper.resetForm();
}
Another alternative approach if resetForm() is not useful.
onSubmit: (values, formikHelper) => {
setTeamData([values, ...teamdata]);
formikHelper.setFieldValue("selectName", "");
formikHelper.setFieldValue("selectAge", "");
formikHelper.setFieldValue("location", "");
}
Here is the modified code sandbox
So I have built a movie search app.
On the 4th page we have the ability to search for a specific movie or TV show.
Now I have built a logic that will display "Movies(Tv Shows) not found" when there are no search results.
Here is the code of the entire "Search" Component:
const Search = () => {
const [type, setType] = useState(0);
const [page, setPage] = useState(1);
const [searchText, setSearchText] = useState("");
const [content, setContent] = useState([]);
const [numOfPages, setNumOfPages] = useState();
const [noSearchResults, setNoSearchResults] = useState(false);
const fetchSearch = async () => {
try {
const { data } = await axios.get(`https://api.themoviedb.org/3/search/${type ? "tv" : "movie"}?api_key=${process.env.REACT_APP_API_KEY}&language=en-US&query=${searchText}&page=${page}&include_adult=false`);
setContent(data.results);
setNumOfPages(data.total_pages);
} catch (error) {
console.error(error);
}
};
const buttonClick = () => {
fetchSearch().then(() => {
if (searchText && content.length < 1) {
setNoSearchResults(true);
} else {
setNoSearchResults(false);
}
});
};
useEffect(() => {
window.scroll(0, 0);
fetchSearch();
// eslint-disable-next-line
}, [page, type]);
return (
<div>
<div style={{ display: "flex", margin: "25px 0" }}>
<TextField className="textBox" label="Search" variant="outlined" style={{ flex: 1 }} color="secondary" onChange={e => setSearchText(e.target.value)} />
<Button variant="contained" style={{ marginLeft: "10px" }} size="large" onClick={buttonClick}>
<SearchIcon color="secondary" fontSize="large" />
</Button>
</div>
<Tabs
value={type}
indicatorColor="secondary"
onChange={(event, newValue) => {
setPage(1);
setType(newValue);
}}
style={{
marginBottom: "20px",
}}
>
<Tab style={{ width: "50%" }} label="Search Movies" />
<Tab style={{ width: "50%" }} label="Search TV Shows" />
</Tabs>
<div className="trending">
{content && content.map(c => <SingleContent key={c.id} id={c.id} poster={c.poster_path} title={c.title || c.name} date={c.first_air_date || c.release_date} media_type={type ? "tv" : "movie"} vote_average={c.vote_average} />)}
{noSearchResults && (type ? <h2>Tv Shows not found</h2> : <h2>Movies not found</h2>)}
</div>
{numOfPages > 1 && <CustomPagination setpage={setPage} numOfPages={numOfPages} />}
</div>
);
};
You can see this in action here.
The problem that happens is that even when I have something in my search results, it still shows the Movies(Tv Shows) not found message.
And then if you click the search button again it will disappear.
A similar thing happens when there are no search results.
Then the Movies(Tv Shows) not found message will not appear the first time, only when you press search again.
I don't understand what is going on. I have used .then after my async function and still it does not execute in that order.
Try adding noSearchResults to your useEffect hook. That hook is what tells React when to re-render, and right now it's essentially not listening to noSearchResult whenever it changes because it's not included in the array.
I have a table showing school name and its fee or rates. There is button in table which opens a drawer containing this component or form.There is a button in table which toggles the state of drawer. As long as i am only reading values in the form by clicking on the button my initalValue updates. But if i update price and submit form for any 1 entry , then initial value my price or school doesn't change for other entries or schools. While my state is changing accordingly which I did check by rendering state. But my initialValue of form doesn't updating accordingly
import React, { useState, useEffect } from 'react'
import { Table, Button, Popconfirm, Drawer, Form, Input, Select, notification } from 'antd'
const { Option } = Select
const layout = {
labelCol: {
span: 10,
},
wrapperCol: {
span: 10,
},
}
const tailLayout = {
wrapperCol: {
offset: 7,
span: 14,
},
}
function SchoolRates({ form, relevantData }) {
const [rates, setRates] = useState(null)
useEffect(() => {
setRates(relevantData)
}, [someCondition])
const updateRates = e => {
e.preventDefault()
form.validateFields((error, values) => {
console.log(error, values, 'forms')
})
return 'Data Updated'
}
return (
<>
<div>
{`${rates?.schoolName}, ${rates?.Price}`}
</div>
<Form {...layout} initialValue={rates} onSubmit={e => updateRates(e)}>
<Form.Item label="Country" style={{ marginBottom: '5px' }} size="small">
{form.getFieldDecorator('schoolName', {
initialValue: rates?.schoolname,
rules: [{ required: true, message: 'Please select a school!' }],
})(
<Select
placeholder="School"
allowClear
size="small"
style={{ borderRadius: 0 }}
>
{schoolList.map(item => (
<Option value={item}>{item}</Option>
))}
</Select>,
)}
</Form.Item>
<Form.Item label="Price / Learner" style={{ marginBottom: '5px' }} size="small">
{form.getFieldDecorator('price', {
initialValue: rates?.price,
rules: [{ required: true, message: 'Please enter price/learner' }],
})(
<Input
size="small"
type="number"
style={{ borderRadius: 0 }}
/>,
)}
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit" size="small">
Save
</Button>
</Form.Item>
</Form>
</>
)
}
export default Form.create()(UpdateRates)
I just want to re render the complete form to update data as soon as I click on other school from my table
To update initialValue form input fields need to be cleaned or reset.
So add form.resetFields()(to reset data in input fields) after successful submission of form.
I want to implement two buttons Select All and Select None inside Autocomplete React Material UI along with checkbox for each option.When Select All button is clicked all the options must be checked and when I click Select None all the options must be unchecked.
How do I implement that ?
<Autocomplete
id={id }
size={size}
multiple={multiple}
value={value}
disabled={disabled}
options={items}
onChange={handleChange}
getOptionLabel={option => option.label}
renderOption={(option, { selected }) => (
<React.Fragment >
{isCheckBox(check, selected)}
{option.label}
</React.Fragment>
)}
renderInput={params => (
<TextField id="dropdown_input"
{...params} label="controlled" variant={variant} label={label} placeholder={placeholder} />
)}
/>
export function isCheckBox(check, selected) {
if (check) {
const CheckBox = <Checkbox
id="dropdown_check"
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
return CheckBox;
}
return null;
}
I stumbled into the same issue earlier today.
The trick is to use local state to manage what has been selected, and change the renderOption to select * checkboxes if the local state has the 'all' key in it.
NB: At the time of writing React 16 is what I'm working with
I'm on a deadline, so I'll leave a codesandbox solution for you instead of a rushed explanation. Hope it helps :
Select All AutoComplete Sandbox
Updated
for React version 16.13.1 and later. codesandbox
const [open, setOpen] = useState(false);
const timer = useRef(-1);
const setOpenByTimer = (isOpen) => {
clearTimeout(timer.current);
timer.current = window.setTimeout(() => {
setOpen(isOpen);
}, 200);
}
const MyPopper = function (props) {
const addAllClick = (e) => {
clearTimeout(timer.current);
console.log('Add All');
}
const clearClick = (e) => {
clearTimeout(timer.current);
console.log('Clear');
}
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
return (
<Autocomplete
PopperComponent={MyPopper}
onOpen={(e) => {
console.log('onOpen');
setOpenByTimer(true);
}}
onClose={(obj,reason) => {
console.log('onClose', reason);
setOpenByTimer(false);
}}
open={open}
.....
....
/>
);
Old Answer
Just customise PopperComponent and do whatever you want.
Autocomplete API
const addAllClick = (e: any) => {
setValue(items);
};
const clearClick = (e: any) => {
setValue([]);
};
const MyPopper = function (props: any) {
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
<Autocomplete
PopperComponent={MyPopper}
...
/>
If you want to make an autocomplete with select all option using react material ui and react hook form, you can implement to Autocomplete like so
multiple: To allow multiple selection
disableCloseOnSelect: To disable the close of the box after each selection
options: Array of items of selection
value: Selected options.
getOptionLabel: The string value of the option in our case is name
filterOptions: A function that determines the filtered options to be rendered on search, in our case we used it to add selectAll checkbox.
renderOption: Render the option, use getOptionLabel by default.
renderInput: To render the input,
onChange: Callback fired when the value changes
Now you can play with selected values using handleChange so once the select is fired check if the selected option is select all if yes then set the newest selectedOptions
<Autocomplete
multiple
disableCloseOnSelect
options={items}
value={selectedOptions}
getOptionLabel={(option) => option.name}
filterOptions={(options, params) => {
const filtered = filter(options, params)
return [{ id: 0, name: selectAllLabel }, ...filtered]
}}
renderOption={(props, option, { selected }) => {
// To control the state of 'select-all' checkbox
const selectAllProps =
option.name === 'Sélectionner Tous' ? { checked: allSelected } : {}
return (
<li {...props}>
<Checkbox checked={selected} {...selectAllProps} />
{option.name}
</li>
)
}}
renderInput={(params) => (
<TextField {...params} label={label} placeholder={label} />
)}
onChange={handleChange}
/>
you can refer to the Autocomplete API to get detailed definition of each item
You can refer to this codeSendBox to check a demo of react material Autocomplete with select all using react material ui version 5 and react hook form verion 7