How can I react to a checkbox in a map? - reactjs

How can I toggle a checkbox in a map when I click on it?
So only set to true or false which one was clicked.
Then I would like to either pack the value of the checkbox, in this case the index, into an array or delete it from it.
const handleChange = (event) => {
const copy = teilen;
setChecked(event.target.checked);
if (event.target.checked)
{
setTeilen(teilen, event.target.value);
} else {
copy.splice(event.target.value, 1);
setTeilen(copy);
}
console.log(teilen);
};
[...]
pdfs.map((p, index) => (
[...]
<Checkbox
value={p._id}
index={p._id}
checked={checked}
onChange={handleChange}
inputProps={{ "aria-label": "primary checkbox" }}
/>
[...]
))}

in your case you can try something like this:
const checkboxes = [
{
name:"a",
value:"a",
checked:false
},
{
name:"b",
value:"b",
checked:false
},
{
name:"c",
value:"c",
checked:false
},
];
const checkedArray = [];
const handleChange = (e) => {
if(e.target.checked){
checkedArray.push(item.value);
checkboxes[i].checked = true;
} else {
if(checkedArray.contains(item.value)){
checkedArray.filter(arrItem => arrItem === item.value);
}
checkboxes[i].checked = false;
}
const render = () => {
return checkboxes.map((item,i) => (
<input type="checkbox"
value={item.value}
name={item.value}
checked={item.checked}
onChange={handleChange}
/>
))
}
and you must keep the data in state to make component re-render.

Related

PrimeReact Autocomplete - Disable option after selection

How to disable the selected option in the list of options in autocomplete PrimeReact?
For example, after selecting an option "Afghanistan", it should be disabled right away.
CodeSandbox:
Code Sample:
export const AutoCompleteDemo = () => {
//...
const searchCountry = (event) => {
setTimeout(() => {
let _filteredCountries;
if (!event.query.trim().length) {
_filteredCountries = [...countries];
} else {
_filteredCountries = countries.filter((country) => {
return country.name
.toLowerCase()
.startsWith(event.query.toLowerCase());
});
}
setFilteredCountries(_filteredCountries);
}, 250);
};
const itemTemplate = (item) => {
return (
<div className="country-item">
<div>{item.name}</div>
</div>
);
};
return (
<div className="card">
<AutoComplete
value={selectedCountry2}
suggestions={filteredCountries}
completeMethod={searchCountry}
field="name"
dropdown
forceSelection
itemTemplate={itemTemplate}
onChange={(e) => setSelectedCountry2(e.value)}
aria-label="Countries"
/>
</div>
);
};
This has been deployed in PrimeReact 9.0.0-beta1 that is now in NPM.
Here is a working Code Sandbox:https://codesandbox.io/s/lingering-darkness-vyhp33
const onChange = (e) => {
e.value.disabled = true;
setSelectedCountry2(e.value);
};

Add autocomplete with multiple and creatable reactjs material ui

I want the users to be able to select multiple tags while also allowing them to add a tag if it does not exist, the examples on the material UI documentation work on the freeSolo option which works on string / object values as options whereas when we use multiple, that changes to an array
How do I implement a multiple creatable with material-ui?
My code:
// Fetch Adding tag list
const [listOpen, setListOpen] = useState(false);
const [options, setOptions] = useState<Tag[]>([]);
const loadingTags = listOpen && options.length === 0;
useEffect(() => {
let active = true;
if (!loadingTags) {
return undefined;
}
(async () => {
try {
const response = await getAllTagsForUser();
if (active) {
setOptions(response.data);
}
} catch (error) {
console.log(error);
}
})();
return () => {
active = false;
};
}, [loadingTags]);
useEffect(() => {
if (!listOpen) {
setOptions([]);
}
}, [listOpen]);
<Autocomplete
multiple
id="tags"
open={listOpen}
onOpen={() => {
setListOpen(true);
}}
onClose={() => {
setListOpen(false);
}}
options={options}
disableCloseOnSelect
getOptionLabel={(option) => option?.name || ""}
defaultValue={
contact?.tags?.map((element) => {
return { name: element };
}) || undefined
}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="Tags" />
)}
/>;
This is just fetching tags from the server and showing them as options, I understand that to be able to allow adding more, I would need to add filterOptions and onChange but, can someone please provide an example on how to deal with array there?
I know this isn't an quick answer but may someone else could use it. Found this Question buy searching an solution. Didn't find one so I tryed myself and this is what I Created and seems it works.
Based on the original Docs https://mui.com/components/autocomplete/#creatable
Complete example:
import React, { useEffect, useState } from "react";
//Components
import TextField from "#mui/material/TextField";
import Autocomplete, { createFilterOptions } from "#mui/material/Autocomplete";
//Icons
const filter = createFilterOptions();
export default function AutocompleteTagsCreate() {
const [selected, setSelected] = useState([])
const [options, setOptions] = useState([]);
useEffect(() => {
setOptions(data);
}, [])
return (
<Autocomplete
value={selected}
multiple
onChange={(event, newValue, reason, details) => {
let valueList = selected;
if (details.option.create && reason !== 'removeOption') {
valueList.push({ id: undefined, name: details.option.name, create: details.option.create });
setSelected(valueList);
}
else {
setSelected(newValue);
}
}}
filterSelectedOptions
filterOptions={(options, params) => {
const filtered = filter(options, params);
const { inputValue } = params;
// Suggest the creation of a new value
const isExisting = options.some((option) => inputValue === option.name);
if (inputValue !== '' && !isExisting) {
filtered.push({
name: inputValue,
label: `Add "${inputValue}"`,
create: true
});
}
return filtered;
}}
selectOnFocus
clearOnBlur
handleHomeEndKeys
id="tags-Create"
options={options}
getOptionLabel={(option) => {
// Value selected with enter, right from the input
if (typeof option === 'string') {
return option;
}
// Add "xxx" option created dynamically
if (option.label) {
return option.name;
}
// Regular option
return option.name;
}}
renderOption={(props, option) => <li {...props}>{option.create ? option.label : option.name}</li>}
freeSolo
renderInput={(params) => (
<TextField {...params} label="Tags" />
)}
/>
);
}
const data = [
{
id: 1,
name: 'Tag1'
},
{
id: 2,
name: 'Tag2'
},
{
id: 3,
name: 'Tag3'
},
{
id: 4,
name: 'Tag4'
},
]

How do i select all checkboxes in Javascript?

I am a beginner with javscript So i will be thankful for explanation.
{isolate_list.map((row) => {
return (
<FormControlLabel
control={
<Checkbox
color="primary"
checked={!!checked}
onChange={toggleCheckbox}
name="checkedA"
>
{" "}
</Checkbox>
}
label={row.isolatename}
>
{""}
</FormControlLabel>
);
})}
and i have this button
<Button
onClick={selectall}
style={{ margin: 50 }}
variant="outlined"
label="SELECT ALL ISOLATES"
>
SELECT ALL ISOLATES
</Button>
Can anyone help how can i use the button to select all checkboxes and in the same time i can select every checkbox alone by clicking on it?
I beginn with this part but i am not sure
const [checked, setChecked] = React.useState(true);
const toggleCheckbox = (event) => {
setChecked(event.target.checked);
};
You should hold checkbox value's in the and give the state value as a property to each. For example
<Checkbox
color="primary"
onChange={toggleCheckbox}
name="checkedA"
value={checked}
>
And then in the onClick function
setChecked();
The simplest implementations(without any form manager):
Declare state to store our checked ids array.
const [checkedIds, setCheckedIds] = useState([]);
implement handler.
const handleCheck = useCallback((id) => {
return () => {
setCheckedIds(prevIds => prevIds.includes(id) ? prevIds.filter(item => item !== id) : [...prevIds, id]);
};
}, []);
render our checkboxes and apply handler.
list.map(({ id, isolatename }) => (
<FormControlLabel
key={id}
control={
<Checkbox
color="primary"
checked={checkedIds.includes(id)}
onChange={handleCheck(id)}
name={`checkbox_${id}`}
/>
}
label={isolatename}
/>)
))
ps. in case if <Checkbox/> props 'onChange' returns callback like this (isChecked: boolean) => {} we can simplify (2) step.
const handleCheck = useCallback(id => {
return isChecked => {
setCheckedIds(prevIds => isChecked ? prevIds.filter(item => item == id) : [...prevIds, id]);
};
}, []);
You may remember that it is React JS and not only JS that we are talking about.
In React you want to control data in the way of a state. There are a lot of ways to do so with check boxes, I'm contributing with one that you can see in the code snippet below:
import React, {useState} from "react";
export default function CheckBoxesControllers() {
const [checkboxes, setCheckboxes] = useState(() => [
{ id: "0", checked: false },
{ id: "1", checked: false },
{ id: "2", checked: false },
]);
const handleUpdate = (event) => {
const { target: {id, checked} } = event;
setCheckboxes(currentState => {
const notToBeUpdated = currentState.filter(input => input.id !== id);
return [
...notToBeUpdated,
{ id, checked }
]
});
}
function toggleSelectAll() {
setCheckboxes(currentState => currentState.map(checkbox => ({...checkbox, checked: !checkbox.checked})));
}
return (
<>
{checkboxes?.length ? (
checkboxes.map((checkbox, index) => {
return (
<input
checked={checkbox.checked}
id={checkbox.id}
key={index}
type="checkbox"
onChange={event => handleUpdate(event)}
/>
);
})
) : <></>}
<button onClick={toggleSelectAll}>Toggle Select All</button>
</>
)
}
This code is meant to serve you as an example of how to work properly with react state in the hook way, but there are other way, as you can see in the Documentation

set State doesn't update the value

I have a problem with set State in dropdown semantic-ui-react.
I am using typescript in my code.
The selected category value doesn't change and always returns an empty string "". How can I fix this?
import debounce from "lodash.debounce";
import { observer } from "mobx-react-lite";
import React, { SyntheticEvent, useContext, useState } from "react";
import {
Dropdown,
DropdownItemProps,
DropdownProps,
Form,
InputOnChangeData,
Popup,
} from "semantic-ui-react";
import { RootStoreContext } from "../../../app/stores/rootStore";
const regex = new RegExp("^[a-zA-Z0-9 ]+$");
interface IProps {
loading: boolean;
}
const PurchaseDetailsFilter: React.FC<IProps> = ({ loading }) => {
const rootStore = useContext(RootStoreContext);
const {
setFilter,
itemCount,
loadPurchaseDetails,
categoryId,
setCategoryId,
} = rootStore.purchaseDetailStore;
const { purchaseCategories } = rootStore.purchaseCategoryStore;
const purchaseCategoriesList: any = purchaseCategories.map((data) => {
return { key: data.id, text: data.name, value: data.id };
});
const categoryOptions: DropdownItemProps[] = [
{ key: "all", value: "all", text: "All" },
].concat(purchaseCategoriesList);
const [selectedCategory, setSelectedCategory] = useState("");
const [filterValid, setFilterValid] = useState(true);
const f = debounce((value: string) => {
if (value !== "" && !regex.test(value)) {
setFilterValid(false);
setFilter(value);
} else {
setFilterValid(true);
console.log(loading);
setFilter(value);
loadPurchaseDetails();
}
}, 500);
const cat = debounce((value: string) => {
console.log(value);
setSelectedCategory(value as string);
console.log(selectedCategory);
setCategoryId((value ==="all" ? "" : value) as string);
loadPurchaseDetails();
}, 500);
const handleOnChange = (
event: React.ChangeEvent<HTMLInputElement>,
{ value }: InputOnChangeData
) => {
f(value);
};
let popupMessage = "";
if (!filterValid) {
popupMessage = "Invalid character.";
} else if (itemCount === 0) {
popupMessage = "No results found.";
}
const handleSelectedCategory = (
event: SyntheticEvent<HTMLElement>,
{value}: DropdownProps
) => {
console.log(value);
setSelectedCategory(String(value));
console.log(selectedCategory);
setCategoryId((value ==="all" ? "" : value) as string);
loadPurchaseDetails();
};
return (
<Form>
<Form.Group>
<Form.Field>
<Popup
trigger={
<Form.Input
placeholder={"Enter a filter."}
name={"filter"}
error={!filterValid}
label={"Filter"}
onChange={handleOnChange}
icon={"search"}
loading={loading}
/>
}
content={popupMessage}
on={"click"}
open={!filterValid || itemCount === 0}
position={"right center"}
/>
</Form.Field>
<Form.Field style={{ marginLeft: "10em" }}>
<label>Category</label>
<Dropdown
//defaultValue="All"
//value={categoryId}
value={selectedCategory}
onChange={handleSelectedCategory}
//defaultValue={categoryOptions[0].value}
placeholder="Choose an category"
options={categoryOptions}
selection
/>
</Form.Field>
<Form.Field>
<label>Project</label>
<Dropdown placeholder="Choose an option" />
</Form.Field>
<Form.Field>
<label>Supplier</label>
<Dropdown placeholder="Choose an option" />
</Form.Field>
</Form.Group>
</Form>
);
};
export default observer(PurchaseDetailsFilter);
maybe what you are looking for is this one:
const handleSelectedCategory = (
event: SyntheticEvent<HTMLElement>,
{value}: DropdownProps
) => {
console.log(value);
setSelectedCategory(String(value));
console.log(selectedCategory);
setCategoryId(String(value ==="all" ? "" : value));
loadPurchaseDetails();
};
notice that I changed value as string to be String(value).
I once fiddle around with TypeScript, but I forget how as string casting works. You might find better explanation here: https://stackoverflow.com/a/32607656/7467018
Have a look at the below code comment
const handleSelectedCategory = (
event: SyntheticEvent<HTMLElement>,
{ value }: DropdownProps
) => {
console.log(value); // If this value is string i.e "Hello World", then just call setSelectedCategory(value);
setSelectedCategory(value as string); // Not required. Just call setSelectedCategory(value) if value is string.
console.log(selectedCategory);
setCategoryId((value === "all" ? "" : value) as string); // Curious, where is setCategoryId froming from?
loadPurchaseDetails();
};

MaterialUI Spinning loader help needed for React/Redux app

import { CircularProgress, FormControl, Input, InputLabel } from
'#material-ui/core';
function toKey(s) {
return s.split("_").map((s, i) => i > 0 ? s.slice(0,1).toUpperCase() +
s.slice(1, s.length) : s).join("")
}
Function to split the returned json object:
function toLabel(s) {
return s.split("_").map((s, i) => s.slice(0,1).toUpperCase() +
s.slice(1, s.length)).join(" ")
}
My class:
class Reports extends Component {
constructor(props) {
super(props);
this.state = {
report: '',
filename: 'my-data.csv',
isLoading: false,
tableHeaderData: [],
reports: [
{ name: 'C3 Report', id: 1, actOn: 'c3'},
{ name: 'C4 Report', id: 2, actOn: 'c4'},
{ name: 'C5 Report', id: 3, actOn: 'c5'}
],
categories: {name: 'Cat 1'},
catSelection: 'Select a Category',
repSelection: 'Select Report Type',
isReportSelected: false,
c4RptFirstInput: '',
c4RptSecondInput: ''
}
}
Not sure about this but went with convention:
componentDidMount () {
const {dispatch, id} = this.props;
}
handleChange (e) {
// this.setState({ input: e.target.value });
}
This is the plugin that I'm using to convert the page into a csv file:
csvHeader () {
const data = this.reportData()
if(data.length === 0) return []
const keys = Object.keys(data[0])
return keys.map((k) => {
const label = toLabel(k)
const key = toKey(k)
return { label, key }
})
}
csvData () {
const data = this.reportData()
if(data.length === 0) return []
const values = Object.entries(data);
const keys = Object.keys(data[0])
const rows = values.map(entries => {
const record = entries[1];
return keys.reduce((acc, key, i) => {
acc[toKey(key)] = record[key]
return acc
}, {})
});
return rows
}
Checks if report or package:
reportData(){
switch(this.state.report) {
case 'channels':
return this.props.channels
case 'packages':
return this.props.packages
default:
return []
}
}
Not sure about this placeholder function but copied it from somewhere:
placeholder () {
return (
<div>
<h1 className="display-3">Reports</h1>
<p className="lead" cursor="pointer" onClick=
{this.loadChannelData}>Svc Configuration</p>
</div>
);
}
Was experimenting with this function but wasn't sure how to use it:
componentWillReceiveProps() {
}
handleCategorySwitch = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({ [name]: value});
console.log(`name ${name}, value ${value}`);
}
This is where the 'subselection' of the second set of drop downs happens:
handleSubselection = (e) => {
this.setState({c4RptSecondInput: e.target.value, })
switch( e.target.value) {
case 'input3':
return this.props.ReportGetAllPackages()
}
}
handleReportSwitch = (e) => {
const selectedAction = e.target.value;
if (selectedAction == 'c3') {
this.setState(prevState => ({
report: 'channels'
,isLoading: true
}), this.props.ReportGetAllChannels)
}
if (selectedAction == 'c4') {
this.setState(prevState => ({
report: 'packages'
}))
}
}
Render function:
render () {
const {filename, reports, catSelection, repSelection, isReportSelected,
c4RptFirstInput, c4RptSecondInput} = this.state;
return (
<div className="reports">
{this.placeholder()}
<div className="flexMode">
<span className="spanFlexMode">
<InputLabel htmlFor="catSelection"></InputLabel>
<Select value={catSelection} name={'catSelection'}
onChange={(e) => this.handleCategorySwitch(e)}>
<MenuItem value="select">Select Category</MenuItem>
<MenuItem value={'Cat1'}>Cat 1</MenuItem>
<MenuItem value={'Cat2'}>Cat 2 </MenuItem>
<MenuItem value={'Cat3'}>Cat 3 </MenuItem>
</Select>
</span>
<span className="spanFlexMode">
<label>Report Name:</label>
<Select value={repSelection} name="repSelection"
onChange={(e) => this.handleReportSwitch(e)}>
<MenuItem defaultValue={'select'}>Select
Report</MenuItem>
{reports && reports.map((report, index) => <MenuItem
key={index} value={report.actOn}>{report.name}</MenuItem>)}
</Select>
</span>
</div>
Below are the second set of drop downs that show up conditionally based on selection of a particular field from above select boxes:
{ this.state.report === 'packages' ? (
<div>
<span>
<label>Input 1:</label>
<Select name="c4RptFirstInput" value={c4RptFirstInput}
placeholder={'Select Provider'} onChange={(e) =>
this.handleSubselection(e)}>
<MenuItem value={'Def'}>Select</MenuItem>
<MenuItem value={'Provider'}>Provider</MenuItem>
<MenuItem value={'Region'}>Region</MenuItem>
<MenuItem value={'Zone'}>Zone</MenuItem>
</Select>
</span>
<span className="spanFlexMode">
<label>Input 2:</label>
<Select name="c4RptSecondInput" defaultValue=
{c4RptSecondInput} value={c4RptSecondInput} onChange={(e) =>
this.handleSubselection(e)}>
<MenuItem value={'Def'}>Select</MenuItem>
<MenuItem value={'input2'}>Input 2</MenuItem>
<MenuItem value={'input3'}>Input 3</MenuItem>
<MenuItem value={'input4'}>Input 4</MenuItem>
</Select>
</span>
</div>
) : null}
<div>
<CSVLink data={this.csvData()} headers={this.csvHeader()}
filename={filename} target={'_blank'}>
<GetAppIcon />
</CSVLink>
Here is where the spinning loader should do it's thing and disappear once the data is loaded - currently it just keeps on spinning and the data never gets loaded even though I can see that the data has successfully come back from the reducer:
{isLoading
? <CircularProgress />
: (
<Table id="t1">
<TableHeaders data={this.csvHeader()} />
<TableContent data={this.csvData()} />
</Table>
)}
</div>
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
ReportGetAllChannels: () => dispatch(ReportGetAllChannels()),
ReportGetAllPackages: () => dispatch(ReportGetAllPackages()),
}
}
const defaultState = ({
state: {},
channels: [],
packages: []
,isLoading: false
})
const mapStateToProps = (state=defaultState) => {
return ({
state: state,
channels: state.RptDetailsReducer.data,
packages: state.RptPackagesReducer.data
,isLoading: false
})
}
isLoading variable is not defined in your render method. I see that you defined it in your component's state and inside your reducer. I assume you are referencing one in your state (Since you said it was keep spinning it is probably the case). You set component's isLoading to true in handleSubselection you have this snippet:
if (selectedAction == 'c3') {
this.setState(prevState => ({
report: 'channels',
isLoading: true
}), this.props.ReportGetAllChannels)
}
This code will set isLoading to true than dispatch ReportGetAllChannels. However your component's state won't be updated. I don't know what ReportGetAllChannels does but I am guessing it sets its own isLoading to false. Which is different variable.
Also you may want to read this https://overreacted.io/writing-resilient-components/#principle-1-dont-stop-the-data-flow. Once you map your state to props you usually want to pass them directly to child components.
Edit:
Quick fix: use this.props.isLoading instead of state, and set isLoading to true inside your dispatched action

Resources