How to Implement Multi Select Dropdown in React - javascript

I'm looking for a good way to create multi select dropdowns in plain React, without using an additional library.
At present, I’m doing something like this:
<select className='yearlymeeting' multiple={true}>
<option value=''>Yearly Meeting</option>
{
this.state.meeting.yearly_meeting.map((m: Meeting, i: number) => {
return (
<option
value={m.title}
key={i}
selected={this.state.selectedTitles.yearlyMeetingTitles.includes(m.title) ? true : false}>
{m.title}
</option>
);
})
}
</select>
This code "works", but I'm getting this warning:
Warning: Use the `defaultValue` or `value` props on <select> instead of setting `selected` on <option>.

From react docs -
You can pass an array into the value attribute, allowing you to select multiple options in a select tag:
<select multiple={true} value={['B', 'C']}>
I think you just need to pass your selected items array to value prop of select element.

Instead of checking conditions and setting "selected" props in the "option" element, directly set the value in the "select" element. The warning should go away.
<select className='yearlymeeting' multiple={true}
value={this.state.selectedTitles.yearlyMeetingTitles}>
<option value=''>Yearly Meeting</option>
{
this.state.meeting.yearly_meeting.map((m: Meeting, i: number) => {
return (
<option
value={m.title}
key={i}
{m.title}
</option>
);
})
}
</select>

Related

Bind the Selected option text To Input box

Im new to react, Im acheiving to search the text,which will post to the api, and the results will be displayed as options in box,for which im using twitter bootstrap just the className's to match with REACT.I want to make the make the option's value to be bind to the input text box above, if i select the option. How to achieve this using useRef and at the same time ,the select box should close if the option is selected.The HandleCHange for the input box triggers the api call for every keystrole
Below is the code for the select box.
<input
className='form-control'
placeholder='Search Drug...'
onChange={handleChange}
/>
<select class='custom-select' size={drugList.drugs?.length> 0 ? '10' : ''} onChange={handleSelectChange}>
{drugList.drugs?.length > 0 &&
drugList.drugs.map((each,index) => {
return (
<option key={index} value={`${each.drug}`}>
{each.drug}
</option>
)
})}
</select>
Here i want the selected value to be bind to the input box.It will be great if you give the sample code or snippet.
Please refer the image above.Thanks in Advance!
I want to make the make the option's value to be bind to the input text box above
---> use component state to store the selected value from the options dropdown
at the same time ,the select box should close if the option is selected.
----> hide/display select options based on input box focus by toggling component state.
created a sandbox for your app, please check
I think you can do this like this.
handleSelectChange = (event) =>{
this.setState({selectvalue:event.target.value});
}
handleChange= (event) =>{
this.setState({selectvalue:event.target.value});
---- and your code here---
}
<input
className='form-control'
placeholder='Search Drug...'
onChange={this.handleChange}
value={this.state.selectvalue}
/>
<select class='custom-select' size={drugList.drugs?.length> 0 ? '10' : ''} onChange={this.handleSelectChange}>
{drugList.drugs?.length > 0 &&
drugList.drugs.map((each,index) => {
return (
<option key={index} value={`${each.drug}`}>
{each.drug}
</option>
)
})}
</select>

How to display only the text in datalist and not the value?

Let suppose that we have the following datalist, and a js variable var carID = '':
<input list="options" #change='${carID = e.target.value}'>
<datalist id="options">
<option value="ID_1">Ferrari</option>
<option value="ID_2">Lamborghini</option>
<option value="ID_3">Jeep</option>
</datalist>
I'd like to show ONLY the car names in my options, and NOT the option values (that are the IDs of the cars), and have the ID of the selected car (the value of the selected option) stored in the variable, not the car name.
I tried different solutions, I post 2 of them (one totally wrong and one right but not complete, I 've found this one in other stack overflow questions):
wrong: it simply doesn't work, e.target.carID is ''.
<input list="options" #change="${carID = e.target.carID}">
<datalist id="options">
<option carID="ID_1" value="Ferrari"></option>
<option carID="ID_2" value="Lamborghini"></option>
<option carID="ID_3" value="Jeep"></option>
</datalist>
Ok it's working, but what if I have 2 cars with the same name and different id? Yes, the second car is ignored and if I select the 2nd car I store the 1st car's ID.
<input id='inputID' list="options" #change='${this.getValue}'>
<datalist id="options">
<option data-value="ID_1" value="Ferrari"></option>
<option data-value="ID_2" value="Lamborghini"></option>
<option data-value="ID_3" value="Jeep"></option>
<option data-value="ID_4" value="Jeep"></option>
</datalist>
js:
getValue(){
let shownValue = this.shadowRoot.getElementById('inputID').value;
let rightValue =
this.shadowRoot.querySelector("#options[value='"+shownValue+"']").dataset.value;
carID = rightValue;
}
I cannot use JQuery. Do you have any solutions? Thanks!
Your code #change='${carID = e.target.carID}' cannot work, as the right hand side of the event handler binding is not callable. You need to wrap it inside an anonymous function, e.g. like so: #change=${(e) => { this.carID = e.target.value }}
That being said, this is what I understood you want to do:
Have a list, where the user can choose from.
In the list, only display the name of the car, not the ID.
Store the selected car's ID in carID, not the name.
I see two ways to do that.
Option 1: Use <select>
If the list of cars is fixed, I think you will be best served using a <select height="1"> element, resulting in a drop down box. Including the little event handler, it looks something like this:
<select #change=${(e) => { this.carID = e.target.value }}>
<option value="ID_1">Ferrari</option>
<option value="ID_2">Lamborghini</option>
<option value="ID_3">Jeep</option>
<option value="ID_4">Jeep</option>
</select>
This will display the text from the text content of the <option> elements, but set the value of the <select> from the <option>'s value attribute, and by the virtue of the onchange event handler will set the carID field on the element.
You can even have two cars with different IDs, but the same name. Note however, that your users would not know, if the display text is the same, which of the two "Jeep" entries to choose. So that might not be a good idea (but I don't know your full use case).
Option 2: Use <input> with <datalist>
Now, if the list of cars is not fixed, i.e. the users are allowed to enter arbitrary data and the selection list is not for limiting their choices, but to help them (prevent typos, speed-up entry) you can use an <input> with an associated <datalist>. But the popup will display both, the <option>'s value and text content (if they are both defined and different). If you insist on only showing the name of the car, not the ID, then the name has to go in the value attribute of the <option> (or the text content). While you could put the ID in the dataset, you really don't need to.
In any case you'll need to map the value string back to the ID through your own code. This will only work if "cars and names" is a one-to-one (aka bijective) mapping, so no two cars with the exact same name would be allowed. (Otherwise your code cannot know which one has been selected just by looking at the name.)
const CARS_BY_ID = {
ID_1: 'Ferrari',
ID_2: 'Lamborghini',
ID_3: 'Jeep',
}
class MyElem extends LitElement {
constructor() {
super();
this.carID = null;
}
render() {
return html`
<input list="myopts" #change=${this.carChanged}>
<datalist id="myopts">
${Object.values(CARS_BY_ID).map((name) => html`<option>${name}</option>`)}
</datalist>`;
}
carChanged(e) {
const name = e.target.value;
this.carID = null;
for (const [key, value] of Object.entries(CARS_BY_ID)) {
if (value === name) {
this.carID = key;
}
}
console.log(`this.carID = ${this.carID}`);
}
}
Note, that in this example the user can e.g. enter "Bugatti" and this.carID will be null.
Also note, that this.carID has not been registered as a lit-element property (it's not listed in static get properties), so there will be no update lifecycle triggered, and no re-rendering happens upon that change.

Ember JS: select list with selected value

I am trying to create a select component. In which I need to select an option based on the value in object.
Found something similar here
Implemented same:
<select>
<option value="" selected disabled >Select </option>
{{#each sourceTypes as |sourceType|}}
<option value={{sourceType.id}} selected={{if (eq sourceType.id selectedOption) 'true'}}>{{sourceType.type}}</option>
{{/each}}
</select>
Here sourceType.id is id for current option and selectedOption is sourceType reference in source object. Type is number in REST service response for both of them.
When I tried to print value of eq sourceType.id selectedOption in option it is giving me false. Then I checked for eq documentation, it is a === b
Why is it giving false even if value and type both are same.
Is there any way to just check for value like a == b.
Is there any way to just check for value like a == b.
You can implement a custom helper that does this (see https://guides.emberjs.com/v2.17.0/templates/writing-helpers/)
import { helper } from "#ember/component/helper"
export default helper(function([a, b]) {
return a == b;
});

Show/hide field based on the value of another field using redux-form

I have a Field which I want to hide based on the value of another Field. For this I am right now using the warn props as I can get the current value as well as the values of other fields in the form.
Is it possible to create custom props ( similar to warn and validate) that can take the current field's value and all the values of the form as arguments?
What are other approaches I can do to hide/show a field based on value of another field using redux-form?
You can use Fields component for this. It handles the state of various fields under a single component.
Example:
// outside your render() method
const renderFields = (fields) => (
<div>
<div className="input-row">
<label>Category:</label>
<select {...fields.category.input}>
<option value="foo">Some option</option>
</select>
</div>
{ fields.category.input.value && (
<div className="input-row">
<label>Sub category</label>
<select {...fields.subcategory.input}>
<option value="foo">Some other option</option>
</select>
</div>
)}
</div>
)
// inside your render() method
<Fields names={[ 'category', 'subcategory' ]} component={renderFields}/>

React defaultValue for Select with Dynamically Generated Options

Use the defaultValue or value props on instead of setting selected on .
<select defaultValue="react">
<option value="react">React</option>
<option value="angular">Angular</option>
</select>
defaultValue would work with the above select tag. However, it does not seem to work with options generated by loop.
<select defaultValue={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
Probably options not fully been set when defaultValue was declared?
I could do manual assigning in componentDidUpdate() and onChange event.
But my question is - Is there any cleaner(better) way to solve it?
Thanks.
This is old, but since answer #1 is related to a controlled Select and the question seems to be related to uncontrolled Select I think is worth to leave some lines for future readers:
The problem is that for uncontrolled components React needs to know what are the options before the first render, since from that moment the defaultValue won't override the current value of the Select. This means that if you render the Select before the options it won't know what to select.
You can solve the problem avoiding the render before the options are available:
const RenderConditionally = ({ options, selected }) => options.length > 0 ? (
<select defaultValue={selected}>
{options.map(item => (
<option key={item.id} value={item.value}>{item.label}</option>
))}
</select>
) : null;
Or without ternary if you desire:
const RenderConditionally = ({ options, selected }) => {
if (options.length === 0) {
return null;
}
return (
<select defaultValue={selected}>
{options.map(item => (
<option key={item.id} value={item.value}>{item.label}</option>
))}
</select>
);
};
For users running into this issue, you can get the desired functionality by using the value prop, instead of defaultValue, e.g.:
<select value={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
Most probably you have something wrong with option_id and options arrays structure, or selectedOptionId variable. The way you build your select component is ok.
I've made a fiddle where this code works fine:
render: function() {
let option_id = [0, 1];
let options = [{name: 'a'}, {name: 'b'}];
let selectedOptionId = 1
return (
<select defaultValue={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
)
}
The best solution that I could find is to set key attribute the same as defaultValue so it will be a different element.
Aftermaths are not researched by me but I believe it should be okay.

Categories