Reactjs: Have one select menu set the value of another - javascript

This is using react. The problem in a nutshell is that I have two select menus in a form (as part of the same component) and I want selecting one to set the options of the other.
One select shows months and one shows days in that selected month. Depending on which month is selected the days should show the correct number for that month (so 31 days in the days select if "January" is selected in month, 28 if "February" and so on).
I cannot get the initial value to change in response to the select. I can log the correct value but what actually appears in the days select never updates from its initial state.
Some code...
I trigger with:
<select defaultValue="Jan" onChange={() => { setMonthNumber(this.refs["month"]["value"]) }} ref="month">
setMonthNumber is an action:
export const setMonthNumber = (monthNumber) => {
store.dispatch({type:'GUEST_INFORMATION_SET_MONTH', message:monthNumber});
}
The reducer that handles that action is:
const initialState = {
currentMonth: "Jan"
};
const guestInformationEditReducer = (state = initialState, action) => {
let localState = merge({}, state);
switch (action.type) {
case 'GUEST_INFORMATION_SET_MONTH':
localState.currentMonth= action.message;
return localState;
}
return state
};
export default guestInformationEditReducer;
I aggregate my reducers and in that file I set (leaving out other reducers):
export default {
guestInformationEditComponent: guestInformationEditReducer
}
and then in my component (named GuestInformationEdit) I connect things with:
const getGuestInformationComponentProps = state => {
return {
month: state.guestInformationEditComponent.currentMonth
}
}
export default connect(getGuestInformationComponentProps)(GuestInformationEdit);
In my component's render method I access the value with:
const days = this.getDaysFromMonth(this.props.month);
getDaysFromMonth is simply a switch that builds a collection of jsx options like:
days.push(<option key={stringNum} value={stringNum}>{stringNum}</option>);
so that I end up with an array of options with the correct values depending on the selected month. This all works (logging shows the correct values). My problem... when I add {days} to my jsx like so:
<select defaultValue="1">
{days}
</select>
I only ever get 31 days, the value set by the initialState in the reducer. It never changes in the view in spite of changing in any console logs I may try. Why? What am I doing wrong/missing? Also, is all of this really necessary for such a change? It seems wickedly complex for such a simple thing.
**edit
In my component I have this outputting the selects:
<BootstrapFormGroup model="guestModel.BirthMonth" classes="col-xs-12 col-md-1 extra-four-percent" formLabel="Birth Month">
<select defaultValue="Jan" onChange={() => { setMonthNumber(this.refs["month"]["value"]) }} ref="month" className="form-control">
<option value="Jan">Jan</option>
<option value="Feb">Feb</option>
<option value="March">March</option>
<option value="April">April</option>
<option value="May">May</option>
<option value="June">June</option>
<option value="July">July</option>
<option value="Aug">Aug</option>
<option value="Sept">Sept</option>
<option value="Oct">Oct</option>
<option value="Nov">Nov</option>
<option value="Dec">Dec</option>
</select>
</BootstrapFormGroup>
{days}
<BootstrapFormGroup model="guestModel.BirthDay" classes="col-xs-12 col-md-1 extra-four-percent" formLabel="Birth Day">
<select defaultValue="1" className="form-control">
{days}
</select>
</BootstrapFormGroup>
See those two instances of {days}? With my above code the first one updates correctly in response to onChange of the month select, the second one does nothing except display the initial state and then never update.

Related

How do I change state on dropdown select in React

for a bit of context I'm trying to make a component navigation menu for my react app. Essentially each dropdown value is equivalent to some components. The way I was going to do this is like follows:
Dropdown menu
<form>
<label htmlFor="#endpointSelect">Endpoint:</label><br/>
<select value={moduleValue} id="endpointSelect" onChange={() => activateModuleValue()}>
<option value="1"> Latest </option>
<option value="2"> Convert </option>
<option value="3"> Historical </option>
<option value="4"> Time Series </option>
<option value="5"> Fluctuation </option>
<option value="6"> Carat </option>
<option value="7"> Lowest-Highest </option>
</select>
</form>
And this is the activateModuleValue()
const [module, setModule] = useState();
// set the module value
const activateModuleValue = () => {
console.log(moduleValue);
}
Eventually there will be a method with a bunch of if statements indicating 1 = <Latest/>, 2 = <Convert/>, etc.
What I was having issues with is correctly grabbing the option values. Using onChange in general I've noticed it returns the value FROM the element I'm changing from instead of changing TO.
Another approach that I'm currently trying is using the value={moduleValue}. Essentially on dropdown select the moduleValue state should change, and ideally I'd have something along the lines of:
useEffect(() => {
activateModule();
}, [moduleValue]);
Where the intent would be to call on the "setActiveComponenet" method whenever the state for moduleValue changes
As an answer to my own question, you have to first set the state and then separately associate state with a component:
// set the module value
const activateModuleValue = (e) => {
setModuleValue(e)
}
// set the module
const activateModule = () => {
if(moduleValue == 1){
setModule(<Latest/>)
}

what is the event that needed to use for multiselect in React

I want to filter a list of movies based on their categories by selecting them using a select menu (select tag), I could do it with a button using on click event it works, but when I want to replace it buy multi-select by changing the event to onChange the filter didn't work.
what do you think?
Thank you
//filter movies based on their categories, this function is created in App.js, it has been passed as props to my CategoriesFilter components
const filterMovie = (category) => {
const filterMovie = MoviesData.filter((movie)=> movie.category === category);
setMoviesList(filterMovie);
}
// this my components
const CategoriesFilter = ({categories, filterMovie}) => {
return (
<div>
<select >
{categories.map((category, index)=> {
return (
<option key={index} onChange={()=> filterMovie(category)}>{category}</option>
)
})}
</select>
</div>
)
}
There are a few options.
You can probably build a custom multi select dropdown component with checkboxes for each item.
You can use third party npm packages like react-select. https://www.npmjs.com/package/react-select
I would suggest you to use the react-select.
You can also use select tag with multiple attribute. But it won't work the way you want it to.
<select name="cars" id="cars" multiple>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
To select multiple items, you'll have ctrl + click the items
Multiple select is <select multiple/>
onchange event is not associated with each option, it is an event of select element.

Why does onChange only work once per value

Each time the web page is loaded the onchange method works, but it only works once per value. When summer is selected the classes are added, when i chose winter the class is added but then if i chosesummer again it will not work. Why is this?
const landingBGR = document.querySelector(".landing-wrapper");
const landingBTN = document.getElementById("l-btn");
function selectedSeason(season) {
switch(season) {
case "summer":
landingBTN.setAttribute("href","summer.html");
landingBGR.classList.add("summer-bgr");
break;
case "winter":
landingBTN.setAttribute("href","winter.html");
landingBGR.classList.add("winter-bgr");
break;
}
}
<select name="" id="landing-drop" onchange="oninput(this.value)">
<option value="">Ireland in the four Seasons</option>
<option value="summer">Summer</option>
<option value="winter">Winter</option>
</select>
try to delete the old class when you select a new option.
landingBGR.classList.remove("summer-bgr");
landingBGR.classList.remove("winter-bgr");
you will maybe need
if(landingBGR.classList.contains("classname"))

How to make a <select> tag in html only shows some data depending on another <select> tag using Vue.js

I'm displaying two <select> elements. The second <select> is disabled depending on the <option> selected on the first <select>. The problem is when I select an <option> on the first <select>, I want the data shown on the second <select> to be changed. For example, if I select District 1 on the first <select>, I want to see john and mary as options in the second <select>, but if I select District 2, I want josef and charles. Consider that I'm doing this on Laravel and using Vue.
I have done the first part using Vue, disabling the second <select> depending on what has been chosen on the first <select> (only third option on the first <select> will enable the second <select>):
https://jsfiddle.net/vowexafm/122/
Template:
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<select #change="treat">
<option value="District1">District 1</option><!--disable 2nd select-->
<option value="District2">District 2</option><!--disable 2nd select-->
<option value="District3">District 3</option><!--enable 2nd select-->
</select>
<br><br>
<select :disabled="isDisabled">
<option>cds 1</option>
<option>cds 2</option>
<option>cds 3</option>
</select>
</div>
Script:
new Vue({
el:'#app',
data: {
isDisabled: true,
},
methods: {
treat: function(e) {
if (e.target.options.selectedIndex == 0 ||
e.target.options.selectedIndex == 1) {
return this.disable();
}
if (e.target.options.selectedIndex != 0 &&
e.target.options.selectedIndex != 1) {
return this.enable();
}
},
enable: function() {
return this.isDisabled = false; // enables second select
},
disable: function() {
return this.isDisabled = true; // disables second select
}
},
});
Now the solution I want,for example: if i'I select District 1 on the first , I want to see john and mary as options in the second , but if I select District 2, I want to see josef and charles on the second .
Populate data object from laravel to have the options for the second select in it and a value for the current selected index from the first select
data: {
secondSelect: [
['john', 'mary'],
['josef', 'charles']
],
currentIndex: 0
}
Define a computed property that returns the values for the second select depending on currentIndex
computed: {
persons () {
return this.secondSelect[parseInt(this.currentIndex)]
}
}
Generate the second select using the computed property persons and use v-model to capture currentIndex.
<div id="app">
<select #change="treat" v-model="selectedIndex">
<option value="0">District 1</option><!--diable-->
<option value="1">District 2</option><!--diable-->
<option value="2">District 3</option><!--unable-->
</select>
<br><br>
<select :disabled="isDisabled">
<option v-for="option in persons" :value="option">{{option}}</option>
</select>
</div>
now the solution I want,for example if i select 'District 1 'on the
first i want to see 'john' and 'mary' as options in the second , but
if I select 'District 2' i want to see 'josef' and 'charles' on the
second .
is that whats in your mind?
new Vue({
el:'#app',
data:{
district:'-1',
options:{
'District1':false,
'District2':false,
'District3':false
},
},
methods:{
getOptions(){
axios.get('https://jsonplaceholder.typicode.com/users',{district:this.district}).then(res=>
this.options[this.district]=res.data)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<select v-model="district" #change="getOptions()">
<option value="-1" selected disabled hidden>choose a district</option>
<option value="District1">District 1</option>
<option value="District2">District 2</option>
<option value="District3">District 3</option>
</select>
<br><br>
<select v-if="options[district]">
<option v-for="option in options[district]">{{option.name}}</option>
</select>
</div>
edit:
after your comment, i edited this answer so now its fetching the data from some api.
to fetch the options from a db, you first have to create an api for your app, and when the request comes out of your vue client side - the server will retrieve rows from the db, do some caculations based on the parameters you sent in the request, and bring back a json data array.
(i wont cover the server side now - thats completely off-topic. but you can easily google for 'laravel json response')
in this snippet, i used some example json api, just to show you how its done on the client side:
i use v-if to cause late- rendering of the second select. it will be rendered only after i get the options, via axios (a very common npm package used to make ajax requests in modern js frameworks).
im also registering an event listener to the change event of the first select - to make my ajax request and populate my options every time the disrict changes (i used a default option to avoid unneeded requests)

Dynamic country states dropdown menu

Hey I'm trying to create a form with a dropdown menu containing states are automatically filtered based on selected country. That is, only show the states of the selected country
JQuery
$("#shipment_sender_attributes_state_code").parent().hide()
$('#shipment_sender_attributes_country_code').change(function() {
states = $("#shipment_sender_attributes_state_code").html()
country = $('#shipment_sender_attributes_country_code :selected').val()
options = $(states).filter("optgroup[label='" + country + "']").html()
$states = $('#shipment_sender_attributes_state_code')
if (options) {
$states.html(options)
$states.parent().show()
} else {
$states.empty()
$states.parent().hide()
}
})
It works the first time I select a country, but if I select another, and then go back, the states variable remains empty and no drop down is shown. Any advice?
Form:
<optgroup label="us">
<option value="AA">Armed Forces Americas (AA)</option>
<option value="AE">Armed Forces Europe (AE)</option>
<option value="AK">Alaska (AK)</option>
<option value="AL">Alabama (AL)</option>
<option value="AP">Armed Forces Pacific (AP)</option>
<option value="AR">Arkansas (AR)</option>
....
<optgroup label="ca"><option value="AB">Alberta (AB)</option>
<option value="BC">British Columbia (BC)</option>
<option value="MB">Manitoba (MB)</option>
<option value="NB">New Brunswick (NB)</option>
<option value="NL">Newfoundland and Labrador (NL)</option>
....
EDIT JSfiddle
Can't quite get the fiddle working, but it's something like this
http://jsfiddle.net/d6cm5v6g/2/
Use var to declare your variables.
var states...
var country...
var options...
If you don't do that you are creating global variables
You need to do a minor change to your code:
$(function(){
$("#shipment_sender_attributes_state_code").parent().hide()
var states = $("#shipment_sender_attributes_state_code").html()
$('#shipment_sender_attributes_country_code').change(function() {
var country = $('#shipment_sender_attributes_country_code :selected').val()
var options = $(states).filter("optgroup[label='" + country + "']").html()
var $states = $('#shipment_sender_attributes_state_code')
if (options) {
$states.html(options)
$states.parent().show()
}
else {
$states.empty()
$states.parent().hide()
}
})
});
You need to move states outside the change function because in your else you do this $states.empty(), so the next time there is no html to filter(all options and optgroups are gone). This way you store the original value in an object.
I change the Afganistan value to "ca" in order for testing purposes(you can switch between Afganistan and United States)
Working fiddle:http://jsfiddle.net/robertrozas/4vxjpjLv/2/

Categories

Resources