I have an html select element that I am filling from a database (List of companies) Data
I am trying to capture the 2 values in the select
The field "company name (alias)" and The CompanyID field
I can't use an index in the map since this list is ordered in a specific way and the id(pk) needs to match the name field
But I can't capture the 2 data
Index
function ManejadorDeCambios(e) {
setformDato((estadoAnterior) => ({
...estadoAnterior,
[e.target.name]: {
pk: e.target.key,
titulo: e.target.value,
},
}));
}
<Console stateName={formDato} />
<label htmlFor="Compania">Compañia:</label>
<Select
name="Dato"
id="Dato"
on_change={ManejadorDeCambios}
db={datos}
/>
select Component
const Select = function ({ name, db, on_change }) {
return (
<>
{Array.isArray(db) ? (
<select name={name} onChange={on_change}>
<option key="0" value="-- Seleccionar --">
-- Seleccionar --
</option>
{db.map((x, index) => (
<option key={x.companiaID} value={x.alias}>
{x.alias}
</option>
))}
</select>
) : null}
</>
);
};
export default Select;
How can I capture the value of key?
One approach would be to change the arguments of ManejadorDeCambios
function ManejadorDeCambios(event, name) {
setformDato((estadoAnterior) => ({
...estadoAnterior,
[name]: {
pk: event.target.key,
titulo: event.target.value
},
}));
}
And change the onChange handler for select accordingly
<select name={name} onChange={(event) => on_change(event, name)}>
Related
So basically if I set country in second dropdown to let's say Spain and then want to change the option in first select dropdown, how can I set second dropdown to go back to default value, in this case All Countries?
<select onClick={handleRankingsRange}>
<option value='top 100'>top 100</option>
<option value='top 200'>top 100-200</option>
<option value='top 200+'>top 200+</option>
</select>
<select onClick={handleFilterCountry}>
<option defaultValue='All Countries'>All Countries</option>
{countries
.filter((country) => country !== '')
.sort()
.map((country, index) => {
return (
<option value={country} key={index}>
{country}
</option>
);
})}
</select>
You need to convert your select components to controlled components by using value and onChange like this:
import { useState } from "react";
const countries = ["Spain", "France", "Portugal", "Germany"];
export default function App() {
const [selectedRange, setSelectedRange] = useState();
const [selectedCountry, setSelectedCountry] = useState();
const handleRankingsRange = (e) => {
setSelectedRange(e.target.value);
setSelectedCountry("");
};
const handleFilterCountry = (e) => {
setSelectedCountry(e.target.value);
};
return (
<div>
<select value={selectedRange} onChange={handleRankingsRange}>
<option value="top 100">top 100</option>
<option value="top 200">top 100-200</option>
<option value="top 200+">top 200+</option>
</select>
<select value={selectedCountry} onChange={handleFilterCountry}>
<option value="">All Countries</option>
{countries
.filter((country) => country !== "")
.sort()
.map((country, index) => {
return (
<option value={country} key={index}>
{country}
</option>
);
})}
</select>
<br />
selectedRange = {selectedRange}
<br />
selectedCountry = {selectedCountry}
</div>
);
}
You can take a look at this sandbox for a live working example of this solution.
[Edit] Added the second part of your question to set back to default "all countries"
if using a list (the list in my example will be an array with objects)
let list = [
{
country: "Spain",
options: ["one", "two", "three"],
},
...
]
you could create a state (useState hook) based on the array.
Mapping that list for the options as you have done, you can also map the first selection based on the array that you would set in your state.
My render would look like this:
return (
<>
<select name="rankings" id="selectRankings">
{ranking.map((rank, index) => {
return (
<option key={index} value={rank}>{rank}</option>
)
})}
</select>
<br/>
<select onChange={(e)=>{handleSelection(e)}} name="countries" id="selectCountry">
<option value="All">All Countries</option>
{list.map((item, index) => {
return(
<option key={index} value={item.country}>{item.country}</option>
)
})}
</select>
< />
);
when selecting a country, on change it will execute handleSelection which will find the correct options and update them to your state.
secondly, if you want to reset the value of the second selection element, you can just set it back to "All" (see code below)
const [ranking, setRanking] = useState(["-- select country first --"]);
const handleSelection = (e) => {
list.find(item => {
if (item.country === e.target.value) {
setRanking(item.options)
}
})
e.target.value = "All";
}
sandbox example
Currently, my dropdown looks like this. I can only select one of the options. I want to be able to select multiple options when clicking. I tried adding multiple in <select> but that doesn't work. How can I make the dropdown allow multiple selections?
const SelectMultipleDropdown = props => {
const {
name,
required,
placeholder,
handleChange,
choices,
value,
fieldValid,
setFieldValid
} = props;
const [currentSelection, setCurrentSelection] = useState("");
// the default value is empty string ""
// invalid/greyed out value is empty string ""
return (
<div>
<p className="field-component-title">
{name}
{required ? <span className="required-star"> *</span> : ""}
</p>
<select
className={`dropdown-select-field field-component-input-box ${
currentSelection === ""
? "dropdown-select-grey"
: "dropdown-select-black"
} ${(() => {
return fieldValid ? "" : "dropdown-select-invalid";
})()}`}
type="text"
onChange={e => {
e.persist();
setCurrentSelection(e.target.value);
handleChange(e);
setFieldValid(true);
}}
value={value}
>
<option value={""}>{placeholder}</option>
{choices.map(({ value, text }, index) => (
<option key={index} value={value}>
{text}
</option>
))}
</select>
</div>
);
};
I'm a bit unclear on the error or undesired behavior you get here. But, here's my try. First multiple works a bit weird depending on browser and OS as described here.
I'm guessing that's not what you're describing tho. I'm guessing the problem is that you are 1. overwriting all your selected and 2. not mapping selected to your options.
So you need to start with an empty array for your selected elements if nothing is selected, then add to that array in the onChange() instead of overwrite the value there and finally add selected to the option when it's in your list of selected elements.
I would also add it appears you are storing the value at this level component and a higher level component thru a callback. It is generally a best practice to store the value in one spot. I'm not sure the best place from this bit of code. That might be best in another question.
const [currentSelection, setCurrentSelection] = useState([]);
return (
<div>
<p className="field-component-title">
{name}
{required ? <span className="required-star"> *</span> : ""}
</p>
<select
className={`dropdown-select-field field-component-input-box ${
currentSelection === ""
? "dropdown-select-grey"
: "dropdown-select-black"
} ${(() => {
return fieldValid ? "" : "dropdown-select-invalid";
})()}`}
type="text"
onChange={e => {
e.persist();
setCurrentSelection((current) => [...current, e.target.value]);
handleChange(e);
setFieldValid(true);
}}
>
<option value={""}>{placeholder}</option>
{choices.map(({ value, text }, index) => (
<option key={index} value={value} selected={currentSelection.includes(value)}>
{text}
</option>
))}
</select>
</div>
);
};
Note: if you want to support super old browsers you'd need to replace includes(currentValue) with indexOf(currentValue) > -1
I think in this component, your current Selection is a string, and use setCurrentSelection(e.target.value); can change currentSelection to another option.
You can change string to array, for instance, currentSelections. And change setCurrentSelections function to:
setCurrentSelections = (e) => {
const value = e.target.value;
let currentSelections = this.state.currentSelections.slice(0);
let index = currentSelections.indexOf(value);
if (index > -1) {
currentSelections.splice(index, 1);
} else {
currentSelections.push(value);
}
this.setState({ currentSelections: currentSelections });
}
And use React Component
class SelectMultipleDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
currentSelections: [],
}
}
setCurrentSelections = (e) => {
const value = e.target.value;
let currentSelections = this.state.currentSelections.slice(0);
let index = currentSelections.indexOf(value);
if (index > -1) {
currentSelections.splice(index, 1);
} else {
currentSelections.push(value);
}
this.setState({ currentSelections: currentSelections });
}
onChange = (e) => {
e.persist();
this.setCurrentSelection(e);
this.porps.handleChange(e);
this.props.setFieldValid(true);
}
render() {
const {
name,
required,
placeholder,
choices,
value,
} = props;
return (
<div>
<p className="field-component-title">
{name}
{required ? <span className="required-star"> *</span> : ""}
</p>
<select
className={''}
type="text"
onChange={this.onChange}
value={value}
>
<option value={""}>{placeholder}</option>
{choices.map(({ value, text }, index) => (
<option key={index} value={value} selected={this.state.currentSelections.includes(value)}>
{text}
</option>
))}
</select>
</div>
);
}
}
I'm trying to loop through the below data structure and set 'Select' fields options. This is where I have an issue. I'm trying to assign 'nameCombined' & 'codeCombined' to the value and text of the Select Form field.
DataStructure:
{
"Bucks":{
"countyCode":"42017",
"globalStateCode":"PA",
"stateCode":"PA",
"nameCombined":"42017 (PA)",
"codeCombined":"42017 PA Bucks"
},
"Montgomery":{
"countyCode":"42091",
"globalStateCode":"PA",
"stateCode":"PA",
"nameCombined":"42091 (PA)",
"codeCombined":"42091 PA Montgomery"
}
}
React JSX
Select Component
const {
name,
options,
actions: { handleFieldChange },
} = props;
<select id={name} onChange={(e) => handleValueChange(e)}>
{options.map((option, idx) => (
<option key={`${option.value}_${idx}`} value={option.value}>
{value === option.text}
</option>
))}
</select>;
// The component that consumes Select Component
// ============================================
<Select
inputData={{
name:name,
options:Object.entries(counties).map(([key, item]) => ({'value': item.nameCombined, 'text': item.codeCombined})), // <- this line needs attention
type:INPUT_TYPES.DROPDOWN,
}},
actions={{
handleFieldChange: handleDropdownChange
}}
/>
Try Object.keys instead of Object.entries.
Object.keys(counties).map(key => ({value: counties[key].nameCombined, item: counties[key].codeCombined}))
The conceptual limitation that you have in your assumption is that you need a proper array to iterate and Object.entries gives you [[key1, object1],...] while Object.keys gives you [key1, key2].
Inside my render I have a select, and for the options I use a .map, like this:
<select value={this.state.eixo} onChange={this.handleChange}>
<option value=""></option>
{this.state.eixos.map((item) => {
return <option key={item.id}>{item.descricao}</option>
})}
</select>
And I want the key from the option that I choose in my handleChange method, I tried something like this but it doesn't seem to be working:
handleChange = (event) => { this.setState({ key: event.target.value }) };
One way to do it is to Set the value of the option to a key:value pair,
<select value={this.state.eixo} onChange={this.handleChange}>
<option value=""></option>
{this.state.eixos.map(item => {
return (
<option key={item.id} value={`${ite.id}:${item.descricao}`}>
{item.descricao}
</option>
);
})}
</select>;
Then split the e.target.value to get the key : value pair and update the state using bracket notation :
handleChange = event => {
const [key, value] = event.target.value.split(":");
this.setState({ [key]: value });
};
If your values could contain : , choose another seperator.
I need to display some attribute of an element in a select, and store the displayed attributes in a value and a different attribute of the same element in another value.
render() {
const refertiItems = this.state.referti.map((referti, i) => {
return (
<option key={referti.hash_referto}>
{referti.tipo_esame}-{referti.data_esame}
</option>
)
});
return(
<Label for="type" text="Descrizione Referto" />
<select
name="descrizioneReferto"
placeholder="Selezionare Referto"
onKeyPress={this.onEnter}
value={this.state.referti.descrizioneReferto}
onChange={this.handleInputChange}
>
<option default value="vuoto"></option>
{refertiItems}
</select>
So in this code i can store {referti.tipo_esame} and {referti.data_esame} in descrizioneReferto. I also need to store {referti.hash_referto} inside codiceReferto. Any advice?
You can access the key of the option element in the onChange function like this:
handleInputChange = (event, child) => {
this.setState({
descrizioneReferto: event.target.value,
codiceReferto: child.key
});
}
To use the material-ui select, change your code to:
<Select
value={this.state.referti.descrizioneReferto}
name="descrizioneReferto"
onKeyPress={this.onEnter}
placeholder="Selezionare Referto"
onChange={this.handleInputChange}>
<MenuItem value="vuoto"/>
{refertiItems}
</Select>
and
const refertiItems = this.state.referti.map((referti, i) => {
return <Menu key={referti.hash_referto}>
{referti.tipo_esame}-{referti.data_esame}
</option>
});