So what i need to do is have the search form of react-select not only react to id an value, but i also want it to be able to search for synonyms of the select options.
For example in a unit of measures list:
{
"id": "MIN",
"name": "minute",
"synonym": "time"
}
I want to be able to type in "min", "minute" or "time" and end up with the selection "minute".
I tried it with pushing another element into the array:
mapUnitOfMeasure()
{
const results = [ ];
for(const c of measureUnits)
results.push({ value : c.id, label : c.name, syn: c.synonym });
}
return results
}
In my render method, the select element looks like this:
<Select
className="react-select"
placeholder=""
disabled={this.state.disabled}
value={this.state.value}
onChange={this.handleChange.bind(this)}
onBlur={this.handleBlur.bind(this)}
searchable={true}
clearable={true}
multi={false}
noResultsText={i18n.getMessage('CurrencyInput.search.form.noResults')}
options={this.mapUnitOfMeasure()} />
)
But "syn" never reacts to a search query.
Anyone have an idea how i can make it work?
I suggest you to use filterOptions props that allows you to create your own filter function.
By default it works like this:
filterOptions={(options, filter, currentValues) => {
// Do no filtering, just return all options
return options;
}}
If you want to make a search in label and syn you can use the function like this:
filterOptions = (options, filterString, values) => {
return options.filter(
x => x.synonym.includes(filterString) || x.label.includes(filterString)
);
};
WARNING
Using filterOptions will override filterOption, matchPos, matchProp, ignoreCase and ignoreAccents options. You will have to take care of those props by yourself.
Here a live example.
Related
React Select palceholder does show up but with some erros. So below is the source code from my project where on click of a button, a modal is displayed where the users are allowed to fill the form. here the drop down list is being displayed where the placeholder for the drop down is not being displayed.
Object
getCountries()
[
{"id":"a6cedb32","name":"America","code":"USA"},
{"id":"042aa7bd0f","name":"Yemen","code":"YE"}
]
useEffect(() => {
if(countryList)
{
let countryDDL = countryList?.map(obj => ({
...obj,
label: obj.code
}))
setCountryDDL(countryDDL)
}
}, [countryList])
return (
<div>
<Select
options={countryDDL}
name="country"
placeholder="I ain't showing up"
/>
</div>
)
}
export function validationSchema() {
return Yup.object({
country: Yup.string().required("This field is required")
});
}
Your obj does not have any code, I'm not sure what are you trying to provide as a value.
Also obj??.code will result an error.
And obj array missing to ','
const obj = [
{ "id":"a654d086",
"label":"A" // here
"name":"A"
},
{
"id":"a654d656086",
"label":"B" //and here
"name":"B"
}
]
Your problem is not in placeholder at all. Your select can't work with provided data. Try something like this:
const [selectedObj, setSelectedObj] = useState();
const handleSelect = (value) => {
setSelectedObj(value)
}
<Select
options={obj}
value={{label: selectedObj.name}}
name="name"
onSelect={handleSelect}
placeholder="Please show up"
/>
This probably might not run, I haven't test it, but the main idea is here. You should select some object from your array of objects (named obj) and display its' value. Currently you are trying to pass name from obj itself and it doesn't have such property at all
I am having a dropdown where a user can add multiple dropdowns and select a value in it. While making a GET request, i wanted to keep the selected value in the dropdown, but not sure how to do it. I am able to make the selected value in a single dropdown, but finding it difficult to keep the selected value in multiple dropdowns.
I will be getting the value in an array like this
values = ["English","Ukraine","Japnese","Korean"];
Then in Select Dropdown for setting the single value i have written it like this:
options = [
{value: "english", label: "English"},
{value: "ukraine", label: "Ukraine"},
{value: "japnese", label: "Japnese"},
{value: "korean", label: "Korean"},
{value: "french", label: "French"}
];
<Select
className="profile-module-select-container"
classNamePrefix="profile-module-select"
options={options}
onChange={selected => {
this.handleDropdownSelect(selected, formKey);
}}
onMenuOpen={(e, i, o) => {
this.setState({
selectMenuOpen: true
});
}}
onMenuClose={() => {
this.setState({
selectMenuOpen: false
});
}}
name={name}
value={options.filter((items) => { return items.value === values })}
/>
values is an array and you would have to loop it over as well and filter it against Options . Considering the current implementation, it would be a nested loop, yes.
I have this kind of objects
{
asignaturaId: 1,
content: {
[question_var]: {
elements: {
text: 'text1',
image: 'img1.jpg'
}
}
[question_var]: {
text: 'text2',
image: 'image2.jpg'
}
}
}
the thing that varies is the [question_var] part and how many objects does content key includes.
I want to be able to dynamically write out all the data on inputs and being able to replace the needed data.
so far I got this:
[...]
<FormControl>
<InputLabel htmlFor="modulo">Asignatura ID:</InputLabel>
<Input id="asignaturaId" name="asignaturaId" value={arrayData[0]} onChange={(e) => setAsignaturaId(e.target.value)} />
</FormControl>
{arrayData.map(pregunta => {
console.log(pregunta)
return (
<>
<FormControl>
<InputLabel htmlFor="texto">Texto:</InputLabel>
<Input id="texto" name="texto" onLoad={() => setTexto(pregunta[1].elements.text)} value={pregunta[1].elements} onChange={(e) => setText(e.target.value)} />
</FormControl>
[...]
what I omited was, I took the object and did Object.entries(myObject) and assigned it to the arrayData array. asignaturaId will be just one key on every object I receive, so I can easily pass that value and change it on an input. The problem is the content, as it is dynamic and change too much, I find it hard to use useEffect() hook to populate my input with the data I receive and manipulating it in the same input without changing the other values with the same keys or straight foward, not being able to edit them at all.
I have 4 days in this problem. please help!
Edit: I am sorry about my english, if I didn't make myself clear, I want to overwrite the object, keeping the data the user doesn't update in the front end.
Declear your json array first in your React hook Component and make sure each object should have unique key
const [yourJson,setYourJson] = useState({
asignaturaId: 1,
content: {
[question_var1]: {
elements: {
text: 'text1',
image: 'img1.jpg'
}
},
[question_var2]: {
text: 'text2',
image: 'image2.jpg'
}
}
});
Add Setter to add new object in your json content
const addNewData = (text, imgSrc) =>{
let newIndex = Object.keys(yourJson.content).length + 1;
let newJson = yourJson;
newJson.content[`question_var${newIndex}`] = {
'text': text,
'image': imgSrc
}
setYourJson({...newJson})
}
And in render callback use this mapping
<FormControl>
{Object.keys(yourJson.content).map(key => (
<InputLabel htmlFor="texto">{yourJson.content[key].text}</InputLabel>
// <Input id="texto" name="texto" ....
))}
</FormControl>
I have three components in my project, App, DataTable and Table.
The App will render a DataTable that contains the Table component. I just called DataTable with data and columns props in it.
// data sample
const tmpData = [
{
name: "Can",
age: 4,
}, {
name: "David",
age: 44,
}, {
name: "Sara",
age: 14,
}, {
name: "Hani",
age: 24,
}
]
// main columns array
const tmpColumns = [
{
title: "Name",
accessor: "name",
}, {
title: "Age",
accessor: "age",
}
]
function App() {
return (
<div className="App" style={{background: "#f0f0f0", padding: "1rem"}}>
<div>App Component:</div>
<DataTable data={tmpData} columns={tmpColumns}/>
</div>
);
}
DataTable component is just for handling selection and filter actions on my table. So, it could manipulate the data and the columns. the Table will render in it and here is the code of DataTable.
function DataTable(props) {
const [data, setData] = useState(props.data)
const [columns, setColumns] = useState(props.columns)
const [selection, setSelection] = useState([])
useEffect(() => {
// add select columns after component mount
handleColumnChange()
}, [])
// listen to selection change
useEffect(() => {
// selection change log, It change on each select.
console.log(selection);
}, [selection])
function handleRowSelect(rowName) {
const keyIndex = selection.indexOf(rowName);
console.log(selection, rowName, keyIndex);
if (keyIndex === -1)
setSelection(preSelection => ([...preSelection, ...[rowName]]))
else
setSelection(preSelection => preSelection.filter(sl => sl !== rowName))
}
function handleColumnChange() {
// add select column if not added already
if (!columns.find(col => col.accessor === 'select')) {
setColumns([
...[{
title: "Select",
accessor: "select",
// this method will execute to render checkbox on Select table
Cell: function (row) {
return <input type="checkbox"
onChange={() => handleRowSelect(row.name, selection)}
checked={selection.includes(row.name)}/>
},
}],
...columns,
])
}
}
return (
<div className="DataTable" style={{background: "#e0e0e0", padding: "1rem"}}>
<div>Data Table:</div>
<Table {...{data, columns}}/>
</div>
)
}
Table component will render columns and suitable data for them. For each column in columns array we have an item to access data (accessor) or an executable method to return custom data (Cell) and here is its code.
function Table(props) {
const [data, setData] = useState(props.data)
return (
<div className="Table" style={{background: "#d5d5d5", padding: "1rem"}}>
<div>Table</div>
<table>
<tbody>
<tr>
{props.columns.map((th, key) => (
<th key={key}>{th.title}</th>
))}
</tr>
{/* generating data rows */}
{data.map((dr, key) => (
<tr key={key}>
{columns.map((col, index) => (
<td key={index}>
{
// the "Cell" method has high priority than "accessor" selector
(col.Cell && typeof col.Cell === "function") ?
col.Cell(dr) :
dr[col.accessor]
}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)
}
As you saw above, to handle row selection I manipulate the columns in the DataTable component by adding a new column at first index of my columns array. Everything works fine for now. But, when I try to select a row, the Call methods of the select column, could not access the selection array state of my DataTable component. and it's my problem!
Actually, on each select, the selection array must update and target checkbox must check. Apparently, there is a copy of the selection that changes (or maybe not changes).
You also could check the whole project on CodeSandbox
I have some fixes to your code, check it out on CodeSandbox.
The idea is that you don't need to put the columns to state, instead you just need to get the columns with the selection box.
I also added a simple optimization to it by implementing React.useMemo to memoized the calculated columns. It will only be re-calculated when the props.columns and selection state changes.
Hope this helps! Happy coding! :)
Ok, your tmpData passed from App to DataTable is read-only. So by design you will not see any change your data along the way.
There're couple of ways to get it working, mostly having something to do to allow your DataTable to pass the change back to App if that happens.
Step 1, you could add one prop called onRowClick on the DataTable,
<DataTable data={tmpData} onRowClick={row => { console.log(row) } />
Step 2, you need to allow your tmpData to change after the event. You are using hooks, so we can
const [tmpData, setTmpData] = useState([sampleData])
return (
<DataTable data={tmpData} onRowClick={row => {
// use setTmpData to adjust the tmpData to get a new instance of tmpData
} />
)
Of course for things with this complexity, we normally use useReducer instead of useState, since there's definitely other things that you want to do other than selecting the row :)
I have this Dropdown menu instance:
<Dropdown
selection
options={this.state.options}
search
value={value}
onChange={this.handleChange}
onSearchChange={this.handleSearchChange}
/>
and when my backend returns response, which is then set as state and it is structured like this:
"options": [
{
"text": "New York,All Airports (NYC) , USA",
"value": "NYC"
},
{
"text": "New York,Newark Liberty Intl (EWR), USA",
"value": "EWR"
},
{
"text": "New York,John F Kennedy (JFK), USA",
"value": "JFK"
},
{
"text": "New York,La Guardia (LGA), USA",
"value": "LGA"
}
]
...I get this warning:
Warning: flattenChildren(...): Encountered two children with the same
key, 1:$BLZ. Child keys must be unique; when two children share a
key, only the first child will be used.
in select (created by Dropdown)
in div (created by Dropdown)
in Dropdown (created by SearchForm)
How do I add keys to these elements to prevent this warning?
So looking at the code for the Semantic UI source for the dropdown component, the render options function converts your passed in options into a array of DropdownItem components:
renderOptions = () => {
const { multiple, search, noResultsMessage } = this.props
const { selectedIndex, value } = this.state
const options = this.getMenuOptions()
if (noResultsMessage !== null && search && _.isEmpty(options)) {
return <div className='message'>{noResultsMessage}</div>
}
const isActive = multiple
? optValue => _.includes(value, optValue)
: optValue => optValue === value
return _.map(options, (opt, i) => (
<DropdownItem
key={`${opt.value}-${i}`}
active={isActive(opt.value)}
onClick={this.handleItemClick}
selected={selectedIndex === i}
{...opt}
// Needed for handling click events on disabled items
style={{ ...opt.style, pointerEvents: 'all' }}
/>
))
}
the key for this array is set by taking the value prop and appending the index to it:
key={`${opt.value}-${i}`}
which should always be unique since the index is used but there is another part of the code for hidden inputs
renderHiddenInput = () => {
debug('renderHiddenInput()')
const { value } = this.state
const { multiple, name, options, selection } = this.props
debug(`name: ${name}`)
debug(`selection: ${selection}`)
debug(`value: ${value}`)
if (!selection) return null
// a dropdown without an active item will have an empty string value
return (
<select type='hidden' aria-hidden='true' name={name} value={value} multiple={multiple}>
<option value='' />
{_.map(options, option => (
<option key={option.value} value={option.value}>{option.text}</option>
))}
</select>
)
}
in this one the key is set to only the value, not the value plus index.
<option key={option.value} value={option.value}>{option.text}</option>
this might be your problem, if you have duplicate values then the key will not be unique. Double check the options list to make sure you don't have duplicate values.