PrimeReact Autocomplete - Disable option after selection - reactjs

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);
};

Related

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'
},
]

Using reactPrime library in DataView components how update dynamic values (react hook)?

how I can update price value when update quantity value automatically ??
page design
interface ui
print values on the console:
print values on the console:
This sentence needs to be modified
{quantity[initQ] == 1 ? data.price : initP[initQ]}
i use setState to save multiple values
export default function Contaner({ setPressed, getPressed }) {
const [products, setProducts] = useState([]);
const [layout, setLayout] = useState('list');
let initQ = 1;
const [initP,setInitP] = useState({ [initQ]: 1 }) ;
const [quantity, setQuantity] = useState({ [initQ]: 1 });
function checkQuantity(e, data) {
if (e.value <= data.quantity) {
initQ = data.name;
setQuantity({ ...quantity, [data.name]: e.value});
setInitP( { ...quantity, [data.name]: data.price * e.value});
console.log(initP );
setCart(current => [...current, data.name]);
}
else {
showError();
}
}
const renderListItem = (data) => {
return (
<div style={{ display: "flex" }}>
<button className="button_color" onClick={() => removeItem(data)}>
<i className="pi pi-trash"></i>
</button>
<h6>{quantity[initQ] == 1 ? data.price : initP[initQ] }</h6>
<InputNumber id="stacked" showButtons min={1} value={quantity[initQ]}
onValueChange={(e) => checkQuantity(e, data)} />
<InputText disabled={true} value={"₪ " + data.price} />
<h6>{data.name}</h6>
</div>
);
}
const itemTemplate = (product, layout) => {
if (!product) {
return <></>;
}
if (layout === 'list') {
return renderListItem(product);
}
}
return(
<DataView value={products} layout={layout} itemTemplate={itemTemplate} rows={1} />
);
}

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

How can I react to a checkbox in a map?

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.

Getting selected items in Fluent UI DetailsList

I am using Fluent UI DetailsList. In the example the component is implemented as a class component but I am using a functional component.
I am having difficulties in getting the selected items, I assume and think my implementation is incorrect. The problem is I do not get ANY selected items.
export const JobDetails = () => {
const { actions, dispatch, isLoaded, currentTabJobs, activeTabItemKey } = useJobDetailsState()
let history = useHistory();
useEffect(() => {
if (actions && dispatch) {
actions.getJobListDetails()
}
}, [actions, dispatch])
const getSelectionDetails = (): string => {
let selectionCount = selection.getSelectedCount();
switch (selectionCount) {
case 0:
return 'No items selected';
case 1:
return '1 item selected: ' + (selection.getSelection()[0] as any).name;
default:
return `${selectionCount} items selected`;
}
}
const [selectionDetails, setSelectionDetails] = useState({})
const [selection, setSelection] = useState(new Selection({
onSelectionChanged: () => setSelectionDetails(getSelectionDetails())
}))
useEffect(() => {
setSelection(new Selection({
onSelectionChanged: () => setSelectionDetails(getSelectionDetails())
}))
},[selectionDetails])
return (
<div>
<MarqueeSelection selection={selection}>
<DetailsList
items={currentTabJobs}
groups={getGroups()}
columns={_columns}
selection={selection}
selectionPreservedOnEmptyClick={true}
groupProps={{
onRenderHeader: props => {
return (
<GroupHeader
{...props}
selectedItems={selection}
/>
)
},
showEmptyGroups: true
}}
/>
</MarqueeSelection>
</div>
)
}
export default JobDetails;
I might have a more simple answer, this example is for a list with 'SelectionMode.single' activated but I think the principle of getting the selected item remains the same
const [selectedItem, setSelectedItem] = useState<Object | undefined>(undefined)
const selection = new Selection({
onSelectionChanged: () => {
setSelectedItem(selection.getSelection()[0])
}
})
useEffect(() => {
// Do something with the selected item
console.log(selectedItem)
}, [selectedItem])
<DetailsList
columns={columns}
items={items}
selection={selection}
selectionMode={SelectionMode.single}
selectionPreservedOnEmptyClick={true}
setKey="exampleList"
/>
I found a solution to the problem I was having and I had to memorize the details list
What I did:
const [selectedItems, setSelectedItems] = useState<IObjectWithKey[]>();
const selection = useMemo(
() =>
new Selection({
onSelectionChanged: () => {
//console.log('handle selection change',selection.getSelection())
setSelectedItems(selection.getSelection());
},
selectionMode: SelectionMode.multiple,
}),
[]);
const detailsList = useMemo(
() => (
<MarqueeSelection selection={selection}>
<DetailsList
items={currentTabJobs}
groups={getGroups()}
columns={columns}
ariaLabelForSelectAllCheckbox="Toggle selection for all items"
ariaLabelForSelectionColumn="Toggle selection"
checkButtonAriaLabel="Row checkbox"
selection={selection}
selectionPreservedOnEmptyClick={true}
groupProps={{
onRenderHeader: (props) => {
return <GroupHeader {...props} selectedItems={selection} />;
},
showEmptyGroups: true,
}}
onRenderItemColumn={(item, index, column) =>
renderItemColumn(item, index!, column!)
}
/>
</MarqueeSelection>
),
[selection, columns, currentTabJobs, activeTabItemKey]
);
return (
<div>
{detailsList}
</div>
)
Put the selection object in a state.
Example:
...
export const Table: FunctionComponent<TableProps> = props => {
const { items, columns } = props
const { setCopyEnabled } = useCommandCopy()
const { setDeleteEnabled } = useCommandDelete()
const onSelectionChanged = () => {
if (selection.getSelectedCount() === 0) {
setCopyEnabled(false)
setDeleteEnabled(false)
}
else if (selection.getSelectedCount() === 1) {
setCopyEnabled(true)
setDeleteEnabled(true)
}
else {
setCopyEnabled(false)
setDeleteEnabled(true)
}
}
...
const [selection] = useState(new Selection({ onSelectionChanged: onSelectionChanged }))
useEffect(() => {
selection.setAllSelected(false)
}, [selection])
...
return (
<ScrollablePane styles={{
root: {
position: 'fixed',
top: 105, left: 285, right: 20, bottom: 20
},
}}>
<DetailsList
items={items}
columns={columns}
selection={selection}
selectionMode={SelectionMode.multiple}
layoutMode={DetailsListLayoutMode.justified}
constrainMode={ConstrainMode.horizontalConstrained}
...
/>
</ScrollablePane>
)
}
I think the main issue here is onSelectionChanged function is getting called twice, second time with empty data. Reason I found is React useState method re-rendering the data. Solution that worked for me here :
Store value in a normal variable instead of state variable(if you don't want to re-render detailslist after this):
let selectedItem = undefined;
const selection = new Selection({
onSelectionChanged: () => {
selectedItem = selection.getSelection()
// console.log(selectedItem)
// You can use selectedItem value later anywhere you want to
// track your selection.
}
})
<DetailsList
columns={columns}
items={items}
selection={selection}
selectionMode={SelectionMode.multiple}
selectionPreservedOnEmptyClick={true}
setKey="exampleList"
/>

Resources