remove option from second select if already selected [Ordered multiple selection] - vue.js

I had select option repeater.
What I want is that when I selected johndoe at the first option, it will no longer display on the second select option.
here's my html
<div id="app">
<h1>Vue JS Multiple Fields Repeater</h1>
<div class="col-sm-6">
<div class="panel panel-default relative has-absolute" v-for="(field, index) in users.usersRepeater">
<button #click="addUsersField" type="button">
Add
</button>
<button #click="deleteUsersField(index)" v-if="field != 0" type="button">
Delete
</button>
<div class="panel-body has-absolute">
<div class="form-group">
<label for="users" class="control-label col-sm-3 text-left">Users {{field}}</label>
<select :name="'users'+index"
class="form-control"
id="users">
<option value="" hidden>Select User</option>
<option value="1">John Doe</option>
<option value="2">Mark Doe</option>
<option value="3">Mae Doe</option>
<option value="4">John Smith</option>
<option value="5">Mae Smith</option>
</select>
</div>
</div>
</div>
</div>
</div>
here's my vue.js
new Vue({
el: '#app',
data: {
users: {
usersRepeater: [{ user: '' }]
}
},
methods: {
addUsersField: function() {
this.users.usersRepeater.push({
user: ''
});
},
deleteUsersField: function(index) {
this.users.usersRepeater.splice(index, 1);
},
}
});
here's the fiddle -> https://jsfiddle.net/0e3csn5y/23/

Ok I have this working now. I liked the question because making an ordered selection is a generic case. But, for me anyway, it wasn't straightforward. The breakthrough was realising that, when you number the choices, the whole state of the component could be encapsulated in one array, allUsers. Available users and choices then become computed properties, based on this array. Moral of the story: get your store right, with no interactions between elements of the store.
My answer weighs in at 130 lines. How long and hard would this be without Vue? Mind boggles.
Stack wants me to post some code, so here's the computed property that generates an array of choices made, in order of their priority, from the all users array...
choices(){
return this.store.allUsers.map((aUser,index)=>{
if(aUser.selection != null)
return {idxAllUsers : index, selection: aUser.selection};
else
return null;
})
.filter(aSelection=>aSelection != null)
.sort((a,b)=>{return a.selection - b.selection})
.map(a=>a.idxAllUsers);
},

I found this one very helpful.

Related

vue composition api cascading dropdown box

I wish to build a from using Vue composition api. And in the form, there would be two drop boxes. When first dropbox item selected, it will return the corresponding option in second dropbox? How could it be achieved in vue?
eg. When selected Avengers in team (first dropbox), it will display ["Captain America", "Iron Man", "Thor", "Hulk", "Black Widow", "Hawkeye"] option in second dropbox.
When selected JLA in team (first dropbox), it will display ["Superman", "Batman", "Wonder Woman", "Flash", "Green Lantern", "Aquaman"] option in second dropbox.
<div class="row mb-3">
<label class="col-sm-2 col-form-label">Favourite Team</label>
<select class="form-select" aria-label="Default select example" onchange="teamSelected(this.value)" name="team">
<option selected>Open this select menu</option>
<option value="Avengers">Avengers</option>
<option value="JLA">Justice League</option>
</select>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label">Favourite Hero</label>
<select class="form-select" aria-label="Default select example" id="superhero" disabled name="superhero">
</select>
</div>
<script>
import { ref } from 'vue';
export default {
name: 'App',
setup() {
const teamSelected = (event) => {
course.value = event.target.value;
};
return {
teamSelected,
};
},
};
</script>
Thanks in advance

Vue JS - Display option 2 of select menu after it is disabled

I am looking for help on how to display the second option in a select drop-down menu after the select menu is disabled.
It is disabled if there are fewer than 2 options left. The first option is the 'Please select' option but I would like it to display the one remaining option which is the second option. i.e. 'Scotland' in the code below. The data is pulled in using an Axios call so I do not know what the value will be.
Any help would be greatly appreciated.
The select menu code
<select disabled="disabled">
<option disabled="disabled" value="">Select nationality</option>
<option value="Scotland"> Scotland </option>
</select>
Vue
computed: {
selectDisabled: function() {
return this.options.length <= 2;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="quantity" :disabled="selectDisabled">
<option disabled value="">Select</option>
<option v-for="option in options" :value="option">{{option}}</option>
</select>
</div>
You need to create a special computed property that will dynamically tell the <select> which option it should show inside itself. <select> show the option that matches the <select>'s value.
So:
When the select is disabled (has less than 2 options) force it's value to be the value of the first listed option (this.options[0]).
When the select is enabled, pass the normal value selected by the user (this.value)
I've implemented the logic you need below (make sure to click "Run snippet"):
const App = {
el: '#app',
template: `
<div>
<!--
Remember that writing v-model="quantity" is the same as writing :value="quantity" #input="quantity = $event"
(or #input="quanity = $event.target.value" if you put in HTML elements)
You can't use v-model="valueFormatted" here because this would be the same as writing
:value="valueFormatted" #input="valueFormatted = $event.target.value"
So that's a mistake, because valueFormatted is a computed and you can't assign to it
(unless you create a special computed with a setter, but that's not what you need right now)
-->
<select :value="valueFormatted" #input="value = $event.target.value" :disabled="disabled">
<option disabled="disabled" value="">Select nationality</option>
<option v-for="option in options" :value="option">{{option}}</option>
</select>
<hr>
<div>
<button #click="options = ['Scotland']">Make the select have 1 item</button>
<button #click="options = ['Scotland', 'Poland']">Make the seelct have 2 items</button>
</div>
</div>
`,
data() {
return {
options: ["Scotland", "Poland"],
value: '',
}
},
computed: {
disabled() {
return this.options.length < 2
},
/*
* If this.disabled is true, returns the value of the first option
* If it's false, it returns the normal value from data (user selected)
*/
valueFormatted() {
//watch out - this computed will return undefined if this.disabled is true and if options is empty
//to avoid that, you can do for example this:
//return this.disabled === true ? (this.options[0] ?? '' ) : this.value;
return this.disabled === true ? this.options[0] : this.value;
},
},
}
new Vue(App);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<html>
<body>
<div id="app" />
</body>
</html>
You're probably going to use this select's value later to make eg. an API call, so make sure to send this.valueFormatted instead of this.value

Array of Dynamic Dependent Select Box in Vue.js

I have an array of Depend select box which contains Universities and courses. Each university has its own course. and I have built the course dropdown which depends on the university. I can successfully get university course from server request but the problem is when I change the select university it's changing all course fields. How I can get rid of the problem please give me some ideas. Thanks
<template>
<form #submit.prevent="handleSubmit">
<div class="col col-md-12">
<div v-for="(interest, index) in interests" :key="index" class="row">
<div class="col col-md-6">
<div class="form-group mb-4">
<label for="select-ins">Interested Universities </label>
<select
v-model="interest.institute_id"
class="form-control"
#change="onChangeUniversity($event)"
>
<option disabled value="">Select a University</option>
<option
v-for="institute in institutes"
:key="institute.id"
:value="institute.id"
>
{{ institute.institute_name }}
</option>
</select>
</div>
</div>
<div class="col col-md-6">
<div class="form-group mb-4">
<label>Interested Course</label>
<select
v-model="interest.course_id"
class="form-control"
#change="onChangeCourse($event)"
>
<option disabled value="">Select a Course</option>
<option
v-for="course in courses"
:key="course.id"
:value="course.id"
>
{{ course.course_name }}
</option>
</select>
</div>
</div>
<div class="col col-md-12 text-right">
<div class="row ml-4">
<div v-show="index == interests.length - 1">
<button
class="btn btn-warning mb-2 mr-2 btn-rounded"
#click.prevent="add"
>
Add
</button>
</div>
<div v-show="index || (!index && interests.length > 1)">
<button
class="btn btn-danger mb-2 mr-2 btn-rounded"
#click.prevent="remove"
>
Remove
</button>
</div>
</div>
</div>
</div>
</div>
</form>
</template>
<script>
export default {
data() {
return {
institutes: [],
courses: [],
interests: [
{
institute_id: "",
course_id: "",
},
],
};
},
mounted() {
axios.get("/institues").then((res) => {
this.institutes = res.data;
});
},
methods: {
onChangeUniversity(event) {
let universityId = event.target.value;
axios.get(`/institute-course/${universityId}`).then((res) => {
this.courses = res.data;
});
},
add() {
this.interests.push({
institute_id: "",
course_id: "",
});
},
remove(index) {
this.interests.splice(index, 1);
},
},
};
</script>
check screenshot
http://prntscr.com/115mkn5
lets start at your mounted hook.
you call for a API to receive all available institutes. so far so good. each institute got is own ID, this is important for later, lets keep that in mind.
now your using a function which will then call on a "change" event, like onChangeUniversity this is a good way on preventing to overload data in a page, nice idea just to fetch data only when they are needed.
then comes the tricky part which makes it difficult for you and everyone else reading your code.
you have this courses array in your data which normally belongs to the related institute. this array should not be handled as a second array apart from institutes, it should be a child of it.
like check this data structur:
institutes: [
{
institute_name: "WhatEver1",
id: 0,
courses: [{ course: 1 }, { course: 2 }, { course: 3 }],
}
]
instead of this:
institutes: [
{
institute_name: "WhatEver1",
id: 0,
},
],
courses: [{ course: 1 }, { course: 2 }, { course: 3 }],
the first option above is a good nested way to display your data as loop inside a loop.
which means you have to push your courses inside your institute of choice with the belonged id.
your onChangeUniversity function should than do something like this:
onChangeUniversity(event) {
let universityId = event.target.value;
axios.get(`/institute-course/${universityId}`).then((res) => {
const foundedId = this.institutes.findIndex(institute => institute.id === universityId)
this.institutes[foundedId].courses = res.data
});
},
after that its much easier to iterate over and display the data inside the options. and i am sure you will not have that issue anymore.
just try it like that first and give feedback.
Update
<div class="form-group mb-4">
<label>Interested Course</label>
<select
v-model="interest.course_id"
v-if="institute.courses.length !== 0" <-------HERE
class="form-control"
#change="onChangeCourse($event)"
>
<option disabled value="">Select a Course</option>
<option
v-for="course in institute.courses"
:key="course.id"
:value="course.id"
>
{{ course.course_name }}
</option>
</select>
</div>
you need a render condition to stop your courses loop from being iterated when there is no data inside.
also make sure you await till the fetching of courses is completed.
async onChangeUniversity(event) {
let universityId = event.target.value;
await axios.get(`/institute-course/${universityId}`).then((res) => {
this.courses = res.data;
});
},
and also in your mounted hook
async mounted() {
await axios.get("/institues").then((res) => {
this.institutes = res.data;
});
},
if you still struggle please give me a CodeSandbox of your current code.

How do I bind each select option for each item in for loop? Vue.js

So I have the following code in vue.js:
<div v-for="guest in guests" :key="guest">
<label for="attendance">Will {{guest}} be attending? </label>
<select v-model="attendance">
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
<br><br>
</div>
Current Output
I want to know what each guest selects and send it to my backend. Guest is an array that gets sent from the previous page. Here is it's code:
created() {
this.guests = this.$route.query.guests;
this.numGuests = this.guests.length;
},
Currently I am just sending each guest by sending this.guest but I am hoping to bind this somehow.
I have no idea how to do this and I do not know if I am searching for the right thing either. Hopefully someone can help me.
you could save it like an object like this
new Vue({
el: '#app',
data: {
guests: [
'steve', 'mark', 'mario'
],
attendance: {}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div v-for="guest in guests" :key="guest">
<label for="attendance">Will {{guest}} be attending? </label>
<select v-model="attendance[guest]">
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
<br>
</div>
<h2>Attendace: {{ attendance }}</h2>
</div>

How do you make default option with V-bind when using an object?

So I have the following code in vue.js:
<template>
...
<div v-for="guest in guests" :key="guest">
<label for="attendance">Will {{guest}} be attending? </label>
<select v-model="attendance[guest]" id='attendance'>
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
<br><br>
</div>
...
<script>
...
data() {
return {
guests: {},
numGuests: 0,
sleepOver: null,
attendance: { },
};
I am trying to make yes default. I've read other solutions that says the v-model overrides it. The solutions I have found seem not to apply to my specific code. I tried putting it in my attendance and it does not work. Any suggestions related to my code?
Hope it will help you fix the issue
Step 1: First correct your model. When you are going with v-for always model it should be an array. so guests should be an array which having the property on name and willBeAttending property
data () {
return {
guests: [{
name: 'Jeba',
willBeAttending: 'yes'
},
{
name: 'Suthan',
willBeAttending: 'no'
}],
numGuests: 0,
sleepOver: null,
}
}
Step 2: Template should be like below
<div v-for="(guest, $index)" in guests" :key="$index">
<label for="attendance">Will {{guest.name}} be attending? </label>
<select v-model="guest.willBeAttending" id='attendance'>
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
<br><br>
</div>
DEMO