I am having a JSON file in below mentioned format and I want to map the elements to second drop down based on the first drop down value.
Can I achieve it by using this syntax
const toolList = datalist.AssetCategory; -- datalist is the JSON file name <br>
const AIAssets = datalist.SelectedValue; -- SelectedValue is an variable.
--- JSON Data
{
"AssetCategory":
[
{
"toolname" :"Industry"
},
{
"toolname" :"Process"
},
{
"toolname": "Technology"
}
],
"Industry":
[
{
"asset" : "Banking"
},
{
"asset" : "HealthCare"
},
{
"asset" : "Telecom"
},
{
"asset" : "Finance & Banking"
},
{
"asset" : "Insurance"
},
{
"asset" : "Manufacturing"
}
],
"Process":
[
{
"asset" : "Accounts Payable"
},
{
"asset" : "Finance & Accounting"
},
{
"asset" : "Human Resources"
}
],
"Technology":
[
{
"asset" : "Excel"
},
{
"asset" : "SAP S/4HANA"
},
{
"asset" : "GUI"
}
]
}
Here's working example of your problem, though for secondary dropdown it doesn't support dynamic keys you'll have to specify them. But for first one you can have as many dynamic keys as you want.
Complete Demo
You can access the values from first dropdown as follows
import React, { useState } from "react";
import data from "./data.json";
export default function App() {
// Set primary keys here
const primaryOptions = Object.keys(data);
// Option selected from first dropdown, initially set to be empty
const [selectedPrimaryOption, setSelectedPrimaryOption] = useState("");
// Array of secondary selected options
const [secondaryOptions, setSecondaryOptions] = useState([]);
// Change the secondary dropdown when first is changed.
const handleChange = e => {
const value = e.target.value;
setSelectedPrimaryOption(value);
if (value) {
// Accessing keys ['asset', 'toolname'] etc. here
const secondaryData = data[value].map(
v => v["asset"] || v["toolname"] || v
);
setSecondaryOptions(secondaryData);
}
};
return (
<div className="App">
<div>
<label>Primary Select</label>
<select value={selectedPrimaryOption} onChange={handleChange}>
<option value="">Select Option</option>
{primaryOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</select>
</div>
<div>
<label>Secondary Select</label>
<select>
{secondaryOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</select>
</div>
</div>
);
}
Related
I have a select tag containing the optgroup and option tags which is multiple.
When I select the items, the state is not updated and the onChange does not work.
I want the value of the option tag to be transferred to state when the item or items are selected !!!.
const FunctionalComponent = () => {
const [mystate , setMyState] = useState([]);
return(
<div>
<select
onChange={(e) => setMyState(e.target.value) }
className='form-control'
multiple='multiple'
value={mystate}
>
{datas.map((obj) => (
return (
<optgroup key={obj.title} label={obj.title}>
{obj.map((o) => (
<option key={o.id} value={o.id}>{o.name}</option>
))}
</optgroup>
);
))}
</select>
</div>
)
}
export default FunctionalComponent
Thanks to those who help me.
Without knowing how datas is structured it's difficult to write code for it, but based on how I think it's structured here some working code.
Intialiase state as an array.
Have your handler get the selectedOptions, and get each option's value. Add that array to your state.
Here datas is an array objects. Each one has a title, and another array of objects containing the option data. map over the main array, and then map over the options array.
const { useState } = React;
function Example({ datas }) {
const [mystate , setMyState] = useState([]);
function handleChange(e) {
const options = e.target.selectedOptions;
const values = Array.from(options, option => option.value);
setMyState(values);
}
return (
<div>
<select
onChange={handleChange}
className="form-control"
multiple="multiple"
value={mystate}
>{datas.map(obj => {
return (
<optgroup
key={obj.title}
label={obj.title}
>{obj.options.map(obj => {
return (
<option
key={obj.id}
value={obj.id}
>{obj.name}
</option>
);
})}
</optgroup>
);
})}
</select>
</div>
);
}
const datas = [
{
title: 1,
options: [
{ id: 1.1, name: 1.1 },
{ id: 1.2, name: 1.2 },
]
},
{
title: 2,
options: [
{ id: 2.1, name: 2.1 },
{ id: 2.2, name: 2.2 },
]
},
];
ReactDOM.render(
<Example datas={datas} />,
document.getElementById('react')
);
form-control { width: 200px; height: 200px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I am trying to change the background color of select element based on which option user selects. I have created this example https://codepen.io/lordKappa/pen/YzrEWXd and here the color changes but it also changes for all select elements not just for the one we changed.
const [selectState, setSelectState] = useState("To-do");
const [taskID, setTaskID] = useState();
function handleDropdownChange(e) {
var taskID = e.currentTarget.getAttribute("data-index");
setTaskID(taskID);
setSelectState(e.target.value);
}
if (selectState === "To-do") {
selectStyle = {
backgroundColor: "red",
};
} else if (selectState === "Done") {
selectStyle = {
backgroundColor: "green",
};
} else {
selectStyle = {
backgroundColor: "yellow",
};
}
return (
<div className="box">
<div>
{elementList.map((task) => (
<div key={task.id}>
<h1>{task.info}</h1>
<select
onChange={handleDropdownChange}
data-index={task.id}
style={selectStyle}
>
<option value="To-do">To-do</option>
<option value="Done">Done</option>
<option value="In progress">In progress</option>
</select>
</div>
))}
</div>
</div>
);
};
The color changes an all dropdowns in the code-pen you provided because they all rely on the same piece of state. You either need to make the state for each dropdown be independent of each other, or separate the dropdown into its own component.
You should create a state for tasks
const [tasks, setTasks] = useState([
{ id: 0, info: "Task 1", status: 'TODO' },
{ id: 1, info: "Task 2", status: 'TODO' },
{ id: 2, info: "Task 3", status: 'TODO' }
]);
Then, everytime you change the selectbox, update status inside tasks state
function handleDropdownChange(e) {
var taskID = e.currentTarget.getAttribute("data-index");
setTasks(tasks.map(task => {
if (taskID !== task.id) return task;
task.status = e.target.value
return task
}));
}
Finally, when rendering tasks we only need to check the task's status and change style
{elementList.map((task) => {
// put your condition here ....
return (
<div key={task.id}>
<h1>{task.info}</h1>
<select
onChange={handleDropdownChange}
data-index={task.id}
style={selectStyle}
>
<option value="To-do">To-do</option>
<option value="Done">Done</option>
<option value="In progress">In progress</option>
</select>
</div>
)
})}
I have been working with simple dropdown, and I keep having this error returned https://prnt.sc/1xdusd2 (I solved the prop problem)
then I read a bit more on that specific problem and turns out this happens when vue instance cannot find your property.
But countries is there and it is within the instance. I can't understand where I went wrong.
So, the idea is to make the dropdown reactive to the countries data I am getting from api.
data exists only on the parent component and I am sending it as a prop to the child component where I am iterating and displaying within the ooption.
can someone help me what is wrong here specifically
drop down component (child component)
<template>
<div>
<select v-for="(country, ) in countries" :key="country">
<option >{{country.name}} </option>
</select>
</div>
</template>
<script>
export default {
name: "DropDown",
props:['countries'],
data() {
return {
selectedOption: null,
};
},
};
</script>
parent component ************
<template>
<div class="step1 flex flex-col">
<h1 class="self-start mb-5">Шаг 1.</h1>
<div class="flex justify-center ">
<h3 class="text-white font-medium text-xl">Выберите страну</h3>
<div class="mx-5" >
<DropDown :countries="countries" />
{{}}
</div>
</div>
</div>
</template>
<script>
//import Button from "./UI/Button.vue";
import DropDown from "./UI/DropDown.vue";
export default {
name: "Step1",
components: {
DropDown: DropDown,
//Button: Button,
},
data() {
return{
// countries: [
// {
// id: "RU",
// name: "Россия"
// },
// {
// id: "DE",
// name: "Германия"
// },
// {
// id: "EE",
// name: "Эстония"
// }
// ],
}
},
methods:{
async fetchCountry (){
const res = await fetch('api/countries')
let countries = await res.json();
return countries
}
},
created() {
}
};
Vue tries to load your country data before the api fetch has finished, to bypass this add an v-if="countries" in your <select v-for="(country, ) in countries" :key="country">, then vue will only try to load it if countries is not null
You need to have countries in your data in order for it to work in the template, try this in your parent:
import DropDown from "./UI/DropDown.vue";
export default {
name: "Step1",
components: {
DropDown,
},
data() {
return {
countries: [],
}
},
methods: {
async fetchCountry() {
const res = await fetch('api/countries')
this.countries = await res.json();
}
},
};
And this in your child
<template>
<select v-model="selectedOption">
<option
v-for="country in countries"
:key="country.name"
:value="country.name"
>
{{ country.name }}
</option>
</select>
</template>
<script>
export default {
name: "DropDown",
props: {
countries: {
type: Array,
default: () => [],
},
},
data() {
return {
selectedOption: null,
};
},
};
</script>
I want this select to have an object value, this works but I can't show the title of the selected object, does anyone know why?
my code is:
const [categoria, setCategoria] = useState("");
const litaCategorias = [
{
"id": 2,
"titulo": "test",
"descripcion": "descripcion",
}];
const handleChangeCategoria = (value) => {
setCategoria(value)
console.log(value)
}
<Select
fullWidth
variant="outlined"
value={categoria.titulo}
onChange={e => handleChangeCategoria(e.target.value)}
labelWidth={0}
placeholder={"Seleccione un destino"}
>
{litaCategorias.map(categoria => {
return (
<option
value={categoria}
key={categoria.titulo}
>
{categoria.titulo}
</option>
);
})}
</Select>
I have double checked the Select component in material-ui. It provides props api value with description:
If the value is an object it must have reference equality with the option in order to be selected. If the value is not an object, the string representation must match with the string representation of the option in order to be selected.
You you should memorize you array litaCategorias. to make reference equality.
Should use useMemo to memorized array.
const litaCategorias = useMemo(() => [ { "id": 2, "titulo": "test", "descripcion": "descripcion", }], []);
I think it's because you're setting the param in your callback in the .map function to categoria which is defined in the state and is initialized to an empty string. This line here - {litaCategorias.map(categoria => {. The option value in the select dropdown is not getting the correct object because of this.
Try something like this instead:
import React, { useState } from "react";
export default function App() {
const [categoria, setCategoria] = useState("");
const litaCategorias = [
{ id: 1, titulo: "test", descripcion: "descripcion" },
{ id: 2, titulo: "test2", descripcion: "descripcion2" }
];
const handleChangeCategoria = (value) => {
setCategoria(value);
console.log(value);
};
return (
<select
value={categoria.titulo}
onChange={(e) => handleChangeCategoria(e.target.value)}
>
{litaCategorias.map((cat) => {
return (
<option value={cat.titulo} key={cat.id}>
{cat.titulo}
</option>
);
})}
</select>
);
}
https://codesandbox.io/s/wild-mountain-0r4p6?file=/src/App.js
I am using antd library as part of react application. For select option values are fetched from api and rendered in this format.
<Select
{...propsForSelect}
>
{this.props.data.map(e =>
<Option
key={e.key || e}
value={e.value || e}
title={e.title}
>
{`${e.key} | ${e.value}`}
/Option>
)}
</Select>
Once user selects an option I want to display only the value and not {${e.key} | ${e.value}} value.
{${e.key} | ${e.value}} is displaying in dropdown and when user selects an option need to show e.value alone.
CodeSandbox: https://codesandbox.io/s/tender-feynman-uhtn7
Is this what you are looking for?
https://codesandbox.io/s/headless-river-1n0e3
Basically I just added a selected property to each genre object, and then in the Select component's onSelect() prop, passed a callback function that updates state and toggles the respective genre's selected property to true.
Then, in the UI, I simply check if the selected property is true for each genre and then conditionally render only the display value in that case.
This is what the optionLabelProp is for.
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Select } from 'antd';
const { Option } = Select;
class App extends React.Component {
state = {
genres: [{"key": 1000, "value": "1", "display": "Action"},
{"key": 1001, "value": "2", "display": "Adventure"},
{"key": 1002, "value": "3", "display": "Comedy"}]
};
render() {
return (
<div className="container">
<Select
style={{width:"50%"}}
optionLabelProp="value"
>
{this.state.genres.map(e =>
<Option
key={e.key}
value={e.value}
>
{`${e.display} | ${e.value}`}
</Option>
)}
</Select>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('container'),
);
class App extends React.Component {
state = {
genres: [
{ key: 1000, value: "1", display: "Action" },
{ key: 1001, value: "2", display: "Adventure" },
{ key: 1002, value: "3", display: "Comedy" }
],
selected: '' // A default value can be used here e.g., first element in genres
};
handleChange = key => {
this.setState({ selected: this.state.genres.find((object) => object.key === Number(key)).value });
};
render() {
return (
<div className="container">
<Select value={this.state.selected} onChange={this.handleChange} style={{ width: "50%" }}>
{this.state.genres.map(e => (
<Option key={e.key}>
{`${e.display} | ${e.value}`}
</Option>
))}
</Select>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));