Vue multiple select form independent from each other - vuejs2

I need a solution how to make as many select form as my option data length and each form must be independent from each other. It means that if i chooce selected value in one form it must do not overide selected value in others form. Also i need pre-set selected value in each form (for the first form it need to show selected first choice, for second = second choice and so on.)
var app = new Vue({
el: "#app",
delimiters: ["[[", "]]"],
data: {
selected: '',
options: [
{ id: 20 , supp_name: 'test1' },
{ id: 21 , supp_name: 'test2' },
{ id: 34 , supp_name: 'supertest' },
]
},
})
<div margin="20px" v-for='option in options'>
<form action="">
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.supp_name">
[[option.supp_name]]
</option>
</select>
<span>Chosen: [[selected]]</span>
</form>
</div>
It display 3 forms but when I make a choice in on one form it override all forms. How could it be fixed?

You will need as many selected references as there are forms.
For example
var app = new Vue({
el: "#app",
delimiters: ["[[", "]]"],
data: {
selected: [], // make selected an array
options: [{"id":20,"supp_name":"test1"},{"id":21,"supp_name":"test2"},{"id":34,"supp_name":"supertest"}]
},
watch: {
options: {
immediate: true,
handler (options) {
// initialise to the "supp_name" from options
this.selected = options.map(({ supp_name }) => supp_name)
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<div v-for="(_, i) in selected">
<form action="">
<select v-model="selected[i]">
<option v-for="option in options" :value="option.supp_name">
[[ option.supp_name ]]
</option>
</select>
<span>Chosen: [[ selected[i] ]]</span>
</form>
</div>
<pre>selected = [[ selected ]]</pre>
</div>
This synchronises the selected array to be the same length as options then binds each form's <select> to the array index.

Related

Bootstrap vue selecting the object from a list of objects b-form-select

Even though I got the solution to my problem I'm slightly unsatisfied with how it's solved. Is there a way to make this simpler? As in, ideally I would have similar html and the only thing I would do is make a call to api to get the teachers and immediately assign them to this.teachers instead of having to iterate through them to make an untyped array of some new object.
HTML:
<b-form-select
v-model="selectedTeacher"
:options="teachers"
value-field="teacher" //NOT teacher.id, I want the object itself
text-field="teacher.name">
</b-form-select>
JS
var dbTeachers: Teacher[] = await getTeachers();
dbTeachers.forEach(teacher => {
this.teachers.push(new Object({
teacher: teacher ,
name: teacher.name
}));
});
You can bind the entire object to the value of an <option> tag. So instead of using the options prop you would manually create the <option> using the v-for directive.
That way you'll get the entire object in the <b-form-select>'s v-model.
new Vue({
el: "#app",
data() {
return {
selectedItem: null,
options: [
{ id: 1, name: 'Mike Macdonald', age: 42 },
{ id: 2, name: 'Larsen Shaw', age: 27 },
{ id: 3, name: 'Jami Cord', age: 81 },
],
}
}
})
<link href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.js"></script>
<div id="app" class="p-3">
<b-form-select v-model="selectedItem">
<option :value="null" disabled>-- Please select an option --</option>
<option v-for="option in options" :value="option">
{{ option.name }}
</option>
</b-form-select>
Selected option: {{ selectedItem }}
</div>

VueJs drop-down first item not selected on initial load

I have a drop-down option working with an array of items. I want to add the first selection/option as Select a Site.
When the element is rendered, the drop-down does not show the Select a Site initially. The drop-down element does have the array in the drop-down options.
The first image shows the initial state of the drop-down (looks
empty, but its not)
The second image is when the drop-down element is
when selected.
How can I the drop-down working with the Select a Site shown as the first option?
<select id="ddSite" name="ddSite" class="form-control m-b-10" v-on:change="onChangeSite($event)" v-model="ddSite">
<option :value="null">-- Select a Site --</option>
<option v-for="option in sites" v-bind:value="option.SiteId">
{{ option.SiteName }}
</option>
</select>
new Vue({
el: '#app',
data: {
sites: [],
ddSite:""
},
mounted() {
axios.get("/api/sites/" + this.companyid)
.then(response => {
this.sites = response.data
});
},
methods: {
onChangeSite: function (e) {
var self = this;
var siteid = e.target.value;
var sitename = e.target.options[e.target.options.selectedIndex].text;
},
In your code, you're binding the select's value to ddSite.
The -- Select a Site -- option has a value of null, but your ddSite data starts off as an empty string.
In order to have that option selected initially, you must init ddSite as null:
new Vue({
el: '#app',
data: {
ddSite: null,
sites: [{
SiteId: 1,
SiteName: 'Google',
},
{
SiteId: 2,
SiteName: 'Facebook',
},
{
SiteId: 3,
SiteName: 'StackOverflow',
},
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select id="ddSite" name="ddSite" v-model="ddSite">
<option :value="null">-- Select a Site --</option>
<option v-for="option in sites" :value="option.SiteId">
{{ option.SiteName }}
</option>
</select>
</div>

issue caused the Row selection overwrite

I asked question on adding/removing row in how to use "v-for" for adding or removing a row with multiple components
However, I got a bug: when I adding a row, the items in the first row filled to second row and when I changed the second row, the 1st row is also overwritten as the same as 2nd row.
i must did sth really wrong.
in .js
var data1={selected: null, items: ["A1","B1"]};
Vue.component('comp1',{
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data:function(){
return data1
}
});
var data2={selected: null, items: ["A2","B2"]};
Vue.component('comp2',{
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data:function(){
return data2
}
});
new Vue({
el: '#app',
data: {
rows: []
},
computed:{
newId(){
return this.rows.length == 0 ? 1 : Math.max(...this.rows.map(r => r.id)) + 1
}
},
methods: {
addRow: function() {
this.rows.push({id: this.newId });
},
removeRow: function(row) {
this.rows.splice(this.rows.indexOf(row), 1)
}
},
});
in .html
<div id="app">
<div v-for="row in rows">
<comp1></comp1>
<comp2></comp2>
<button #click="removeRow(row)">Remove Row</button>
</div>
<button #click="addRow">Add Row</button>
</div>
You need to add the key.
<div v-for="row in rows" :key="row.id">
And you shouldn't share the data between the components, so move your data into the components.
data: function() {
return {
selected: null,
items: ["A1", "B1"]
}
}
Here is a working version.
Vue.component('comp1', {
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data: function() {
return {
selected: null,
items: ["A1", "B1"]
}
}
});
Vue.component('comp2', {
template: ` <select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}
</option>
</select>`,
data: function() {
return {
selected: null,
items: ["A2", "B2"]
}
}
});
new Vue({
el: '#app',
data: {
rows: []
},
computed: {
newId() {
return this.rows.length == 0 ? 1 : Math.max(...this.rows.map(r => r.id)) + 1
}
},
methods: {
addRow: function() {
this.rows.push({
id: this.newId
});
},
removeRow: function(row) {
this.rows.splice(this.rows.indexOf(row), 1)
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div v-for="row in rows" :key="row.id">
<comp1></comp1>
<comp2></comp2>
<button #click="removeRow(row)">Remove Row</button>
</div>
<button #click="addRow">Add Row</button>
</div>
Specifically, the way you are sharing data is because you are defining the data like this:
var data1={selected: null, items: ["A1","B1"]};
And returning that object from your data function in the component:
data:function(){
return data1
}
This means that every instance of that component is sharing the same data. That's not what you want with components. Each component should have it's own copy of the data. In this case, there is no need whatsoever to define the data object returned from the data function outside the component.

how to use "v-for" for adding or removing a row with multiple components

i have a row with 3 components(in which is a defined component 1, component 2 and component 3, as showed in my previous question:
VueJs component undefined )
how can i add a row or remove a row (in which has 3 components) using v-for?
var data1={selected: null, items:["A", "B"]};
Vue.component('comp1', {
template: `<select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}</option>
</select>`,
data:function(){
return data1
}
});
<!---similar for component 2 and 3--->
new Vue({
el: '#app',
data: {
rows:[]
},
methods:{
addRow: function(){
this.rows.push({});
},
removeRow: function(row){
//console.log(row);
this.rows.$remove(row);
}
},
});
in .html
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div v-for ="row in rows">
<comp1></comp1>
<comp2></comp2>
<comp3></comp3>
<button #click="addRow">Add Row</button>
<button #click="removeRow(row)">Remove Row</button>
</div>
</div>
The code is pretty close. Try this.
console.clear()
const template = {
template: `<select v-model="selected">
<option disabled value="">Please select</option>
<option v-for="item in items" :value="item">{{item}}</option>
</select>`,
data: function() {
return {
selected: null,
items: ["A", "B"]
}
}
};
Vue.component("comp1", template)
Vue.component("comp2", template)
Vue.component("comp3", template)
new Vue({
el: '#app',
data: {
rows: []
},
computed:{
newId(){
return this.rows.length == 0 ? 1 : Math.max(...this.rows.map(r => r.id)) + 1
}
},
methods: {
addRow: function() {
this.rows.push({id: this.newId });
},
removeRow: function(row) {
this.rows.splice(this.rows.indexOf(row), 1)
}
},
});
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div v-for="row in rows" :key="row.id">
<comp1></comp1>
<comp2></comp2>
<comp3></comp3>
<button #click="removeRow(row)">Remove Row</button>
</div>
<button #click="addRow">Add Row</button>
</div>
This code moves the add row button outside the loop, because you don't really need multiple add row buttons. Additionally, it adds a key for each div in the loop so that Vue can properly remove components when necessary. In order to generate the key, the code creates an id property for each new row object.

Retrieving text of select element

When binding a <select> element using vue.js's v-model, how would you get the selected option text as opposed to the selected option value?
In HTML:
<select v-model="selected" options="myOptions"></select>
In JS:
myOptions: [{ text: 'Blue', value: '1' }, { text: 'Green', value: '2' }]
What I would want to retrieve is both the text 'Blue' as well as the value '1' by doing something like {{ selected.text }} or {{ selected.value }}. However, you can only do {{ selected }} which returns the selected value by default.
Ref: Vue.js guide for Dynamic Select Options
You can just use a filter, like this:
html:
<div id='vm'>
Formatted value:<b> {{city | cityFormatter}} </b><br/>
<br/>
<select v-model="city" options="cities"></select>
</div>
js:
var vm = new Vue({
el: '#vm',
data: {
city: 'city1',
cities: [{text: 'Toronto', value: 'city1'},
{text: 'Orleans', value: 'city2'}]
},
filters: {
cityFormatter: function(val) {
var newVal = '';
this.cities.map(function(el){
if (val == el.value){
newVal = el.value + ' ' + el.text;
}
});
return newVal;
}
}
});
Working example:
http://jsfiddle.net/qfy6s9Lj/9/
Actually, you could try combine jquery or just native js code
The solution with jQuery
html:
<div id='example'>
<select v-model="selected" options="myOptions"></select>
</div>
js:
var vm = new Vue({
el: '#example',
data: {
...
},
computed: {
selectedtext: {
cache: false,
//get selectedtext by jquery
get: function(){ return $(this.$el).find(":selected").text();}
}
},
});
The solution without jquery
html:
<div id='example'>
<select ref="ddl" v-model="selected" options="myOptions"></select>
</div>
js:
var vm = new Vue({
el: '#example',
data: {
...
},
computed: {
selectedtext: {
cache: false,
//get selectedtext directly
get: function(){
var ddl = this.$refs.ddl;
return ddl.options[ddl.selectedIndex].text;
}
}
},
});
Moreover, you could create a component for reusing logic and achieve the purpose of accessing the selected value by {{ selected.text }} or {{ selected.value }}.
An answer for Vue 2+
I was interested to come across this question as I'm currently evaluating Vue and React, and researching the comparative ease of getting current selection (not just dropdowns, but the UI generally).
What I found was that things have changed a lot since these posts in May-July 2015, when the latest version of Vue was 0.12. The JSFiddle in #swift's answer still works today because it pulls in Vue 0.12.
Using today's Vue, currently version 2.6, I found a solution that's relevant to people facing the same question today. It's interesting that using 2.6, the markup under discussion won't even get as far as initializing the options:
<select v-model="selected" options="myOptions"></select>
After digging into this, I found that although options is a valid HTML DOM property of the select element, and hence is accessible from JavaScript, it seems Vue no longer supports initializing it in markup like this. Instead, we add traditional HTML option elements. Per https://v2.vuejs.org/v2/guide/forms.html:
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
On first sight, this change seems a backward step. But in fact, remembering that we can use v-for and v-bind, I think it makes things more flexible. To demonstrate why I think that, I will first show the example given at the same linked page:
HTML
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
JS
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
It can be seen in the HTML that this binds the selected value of the dropdown to the Vue instance's selected property (using v-model="selected"), binds the individual option values to the value of each option (using v-bind:value="option.value"), and finally binds the individual option texts to the text that will be displayed (using {{ option.text }}).
It's only a small step further to bind selected to a different option property, be it text, id or whatever properties your option object may have, or--and here's the thing--to the option object itself. Being able to access the option itself as the selected value means we can access all of its properties, rather than only the property we chose to bind to:
HTML
<div id='vm'>
<select id="ddl1" v-model="ddl1selecteditem">
<option v-for="option in options1" v-bind:value="option">
{{ option.txt }}
</option>
</select>
<span>selected item: text='{{ ddl1selecteditem.txt }}', id={{ ddl1selecteditem.id }}</span>
</div>
JS
var vm = new Vue({
el: '#vm',
data: {
options1: [
{ txt: 'One', id: 1 },
{ txt: 'Two', id: 2 },
{ txt: 'Three', id: 3}
],
ddl1selecteditem: {}
}
});
vm.ddl1selecteditem = vm.options1[0];