Reputation: 1151
I'm trying to find a way to bind array of objects within Vue select-element. The case is somewhat as follows:
data: {
ideas: [
{ id: 1, code: "01A", text: "option 1", props: [] },
{ id: 2, code: "02A", text: "option 2 , props: [{ details: "something" }]}
]},
currentForm: {
something: "foo",
else: "bar",
ideaCode: "01A",
text: "option 1"
}
];
... and in HTML ...
<select v-model="currentForm.ideaCode" @change="setCodeAndLabelForForm(???)">
<option v-for="i in ideas" value="i">{{ i.text }}<option>
</select>
Basically I need to be able to track which object user selects, trigger my own change-event, all the while having binding with a single key from another object... selected value / reference-key should be separated from user-selected option/object. Note: currentForm is not same object-type as option! It only contains some of those properties which option happens to have, and which I'm trying to transfer to options by triggering change-event for user-selection.
The problem is I haven't figured out how to pass currently selected value for the function OR how to write something like:
<select v-model="selectedIdea" @change="setCodeAndLabelForForm" :track-by="currentForm.ideaCode">
<option v-for="i in ideas" value="i">{{ i.text }}<option>
</select>
One possible (and working) approach is:
<select v-model="currentForm.ideaCode" @change="setCodeAndLabelForForm">
<option v-for="i in ideas" value="i.ideaCode">{{ i.text }}<option>
</select>
setCodeAndLabelForForm: function() {
var me = this;
this.ideas.forEach(function(i) {
if(i.ideaCode == me.currentForm.ideaCode) {
me.currentForm.ideaCode = i.selectedIdea.ideaCode;
me.currentForm.text = i.text;
... do stuff & run callbacks ...
}
});
}
... but it just seems terrible. Any better suggestions?
Upvotes: 5
Views: 23351
Reputation: 2453
A little bit better workaround: use index in v-for
<select v-model="selIdeaIndex" @change="setCodeAndLabelForForm">
<option v-for="(i,idx) in ideas" value="idx">{{ i.text }}<option>
</select>
For the js:
data: {
selIdeaIndex:null,
ideas: [
{ id: 1, code: "01A", text: "option 1", props: [] },
{ id: 2, code: "02A", text: "option 2", props: [{ details: "something" }]}
]
},
methods:{
setCodeAndLabelForForm: function() {
var selIdea = this.ideas[this.selIdeaIndex];
//Do whatever you wanna do with this selIdea.
}
}
Upvotes: 3
Reputation: 1151
It's been a while since I asked this question and there have been good suggestions for handling this situation. I cannot remember the exact business-case presented here, but just by a quick glance it looks like I couldn't figure out how to set/initialize right selection afterwards, because handling just the @change event is childs play -- it's the pairing of one single value against list of object-based-options which is harder. What I was most likely looking for was something AngularJS used to have (track-by -property, which matches any given value against selected-option).
Personally now-a-days I would separate UI-logics instead of trying to force 'em to blend together. Most viable approach for myself would be handling list of options and the selected option as one logical area (ie. data: { list: [...options], selectedIdea: Object }) and separate the "currentForm"-object from selection. Let's break this out:
So by these statements I guess I would change my select's v-model to be exactly what it's supposed to be: One of the selected objects (change v-model="currentForm.ideaCode" into v-model="selectedIdea" or such). Then I would simply add watcher for that selectedIdea and make any alterations to currentForm-object from there.
How about initializing that option by currentForm.ideaCode ? Do one of the following on create-method:
Another way would be by using computed value, as suggested Augusto Escobar. Also $ref would work, as suggested by feng zhang, but this approach would still require solution for initializing correct option afterwards (when loading editor with initial values). Thanks to Bhojendra Rauniyar as well -- you were right all along, but I just couldn't comprehend the answer as I couldn't have figured out how to backtrack initial selection.
Thanks for all the suggestions over the year!
Upvotes: 0
Reputation: 1243
A humble way is using $ref
.
There is a solution using $ref
and @change
.
Vue.js get selected options' raw object
Upvotes: 0
Reputation: 135762
If you know your option
s will only come from that v-for="i in ideas"
then the <option>
indexes will be the same as the item
indexes.
Thus <select>.selectedIndex
will be the index of the selected this.item
.
new Vue({
el: '#app',
data: {
ideas: [
{ id: 1, code: "01A", text: "option 1", props: [] },
{ id: 2, code: "02A", text: "option 2" , props: [{ details: "something" }]}
],
currentForm: {ideaCode: "01A", text: "option 1"}
},
methods: {
setCodeAndLabelForForm: function(selectedIndex) {
var selectedIdea = this.ideas[selectedIndex];
this.currentForm = {ideaCode: selectedIdea.code, text: selectedIdea.text};
}
}
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
<select v-model="currentForm.ideaCode" @change="setCodeAndLabelForForm($event.target.selectedIndex)">
<option v-for="i in ideas" :value="i.code">{{ i.text }}</option>
</select>
<br> currentForm: {{ currentForm }}
</div>
Differences from yours: @change="setCodeAndLabelForForm($event.target.selectedIndex)"
and the setCodeAndLabelForForm
implementation.
Upvotes: 0
Reputation: 36
I don't know if this is the best solution, but I solve this problem using computed properties like this:
In the JavaScript file (ES6):
data () {
return {
options: [
{ id: 1, text: "option 1" },
{ id: 2, text: "option 2" }
],
selectedOptionId: 1
}
},
computed: {
selectedOption () {
return _.find(this.options, (option) => {
return option.id === this.selectedOptionId
});
}
}
In the HTML file:
<select v-model="selectedOptionId">
<option v-for="option in options" :value="option.id" :key="option.id">{{ option.text }}<option>
</select>
The '_' symbol is a common JavaScript library called Lodash and I highly recommend the usage. It can make you save some precious time.
Upvotes: 0
Reputation: 85545
You can implement like this:
Create empty object data to track the selected value:
currentForm: {}
Watch currentForm on the model and pass the selected object:
<select v-model="currentForm" @change="setCodeAndLabelForForm(currentForm)">
Pass in the selected value in option
: (you were doing right in this step, but I just changed i
to idea
as it's little confusing looping index)
<option v-for="idea in ideas" :value="idea">{{ idea.text }}<option>
Apply your method:
setCodeAndLabelForForm(selected) {
// Now, you have the user selected object
}
Upvotes: 5