Does one select tag can have two v-model? - vue.js

I have this collection called servers which is like this: {id, name, code}.
I have this select tag, I iterate over my servers collection and the data() with the model values.
<select v-model="serveur">
<option v-for="server in servers" :key="server.id" :value="server.name">
{{server.name}}
</option>
</select>
.
.
.
data () {
return {
serveur:'',
code:''
}
}
The value "server.name" selected is binded to the model "serveur".
All of this works just fine, but I want the value "server.code to be binded to the model "code"
I can't figure out how to do this. I'm new to vuejs, like I've been working with vue for the first time only two days ago.
This is also my first Stack Overflow "asks" so I hope I am being clear enough.
Thanks in advance.

You really have one independent data item, which I will call selectedServer. It gets the value of the server entry, which contains both name and code.
<select v-model="selectedServer">
<option v-for="server in servers" :key="server.id" :value="server">
{{server.name}}
</option>
</select>
You can simply refer to selectedServer.name and selectedServer.code in the URL you need to assemble from them, or you can create computeds to return those values under whatever names you prefer, like
data: {
selectedServer: {}
},
computed: {
serveur() { return this.selectedServer.name; },
code() { return this.selectedServer.code; }
}

To achieve this, you can bind the select with an array containing 'serveur' and 'code'. But you can't bind the select with multiple variables directly.

You can do comething like that:
<select #input="serveur = $event.name; code = $event.code">
<option v-for="server in servers" :key="server.id" :value="server">
{{ server.name }}
</option>
</select>
If you need fiddle - feel free to ask. It is possible that this example not working :)

I believe you can find an answer here:
https://stackoverflow.com/a/50982485/10954996
that is something I would do - create change handler and change everything I need within handler method
I hope this helps

Related

Vuejs 3 alternative select binding

I'm trying to get a select element bound to a value for a custom object. The crux here is that the object property in question has a custom getter. The value is set as a number, but when accessed returns an associated value as a string. Why I do this is a long story.
So I have an object of key-value pairs making some options:
<select v-model="myObject.myProperty">
<option v-for="v, k in myOptions" :key="k" :value="k">{{v}}</option>
</select>
{{myObject.myProperty}} //this line prints out the correct value
But the options are not showing as selected. The value is updated for myObject.myProperty and it returns what I expect. I suspect that behind the scenes, it's correctly assigning k to my custom object, but that because it returns a different string value, Vue can't inherently figure out which option to mark 'selected'.
Manually adding :selected does not help:
<option v-for="v, k in myOptions" :key="k" :value="k" :selected="v === myObject.myProperty">{{v}}</option>
I also tried to manually bind the select instead of using the v-model attribute, also no:
<select :value="myObject.myProperty" #input="myObject.myProperty = $event.target.value"
Is there an alternative way to wire up a select/option situation? If not, building a custom component with faux-select functionality is my next step.
For clarity, myOptions is a key-value like this
{
0 : 'Option 1',
1 : 'Option 2',
}
But myObject has special setters that take and remember the key, then also a special getter than returns the value from myOptions.
So then:
myObject.myProperty = 0;
console.log(myObject.myProperty) //logs 'Option 1'
When when I set the value to the key (k) I get back the corresponding value when the option is selected and the value of 'myObject.myProperty' is what I expect. Example: I pick 'Option 1' from the drop-down, which has a value of 0 derived from the key k.
However, although myObject.myProperty has the value I want, I can't get Vue to display the the actual html option as selected, probably because the value returned by myObject.myProperty is 'Option 1' and not 0
Alright, the actual answer:
<select #input="myObject.myProperty = $event.target.value">
<option v-for="v, k in myOptions" :selected="myObject.myProperty === v" :key="k" :value="k">{{v}}</option>
</select>
v-model won't work here because it simply doesn't care that you've manually applied selected: it will always try to match the option value to the the v-model property value. As this object takes one value with a setter and returns another with a getter, these will never align.
Instead, manually assign the value to the object with #input and match the value from the getter to the value in the options for selected.
Not sure I understand your question properly or not. Hence, adding my input on your requirement below.
As myObject.myProperty returning the value you passed in the select and as per your code you are passing the index as value.
Hence, while comparing in :selected both LHS and RHS should contain index of the item you passed.
Working Demo :
const app = new Vue({
el: '#app',
data() {
return {
myOptions: [{
id: 1,
name: 'alpha'
}, {
id: 2,
name: 'beta'
}, {
id: 3,
name: 'gama'
}],
myObject: {
myProperty: ''
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="myObject.myProperty">
<option v-for="(item, index) in myOptions" :selected="myObject.myProperty === item.id" :key="item.id" :value="item.id">{{item.name}}</option>
</select>
{{myObject.myProperty}}
</div>
UPDATE Based on the newly provided information.
In Template:
<select v-model="selectedOption">
<option v-for="(v, k) in options" :key="k" :value="k">
{{ v }}
</option>
</select>
In JS
data() {
return {
selectedOption: null,
options: {
0: 'Option 1',
1: 'Option 2',
},
};
}
By setting selectedOption with the key of options will correctly display the selected option.
Here is a link to a working example using your data structure. It contains two selects both using the same object for its data source. One with a default selection. the other without.
https://stackblitz.com/edit/vue-efekym?file=src/App.vue
THIS IS NOW OUTDATED BASED ON THE LATEST INFO
Based on the logic in your further examples I think the problem is within conflating your k & v variables in your loop. Although it is hard to tell because there isn't any sample data or a complete isolated component outlying the behavior.
However in your later examples you have:
<option v-for="v, k in myOptions" :key="k" :value="k" :selected="v === myObject.myProperty">{{v}}</option>
I am inferring that you believe that myObject.myProperty is holding the value from variable v, when in fact you are setting the value as variable k as witnessed in :value="k"
By correcting this I believe your issue will be resolved, you also noted a int to string conversion. depending on how/where this is happening this could also contribute to your headaches because this will never equate to true. '1231' === 1231

Vue.js: binding select boxes, but don't want to ajax all the options

Good day. I'm using Vue.js to render an arbitrary number of select elements from the data in a component.
Here's sample JSON data that indicates there are two select elements, each with one or more options.
{
"dropdowns":[
{
"cd":"UG9ydGZvbGlv",
"formname":"sp_filter_UG9ydGZvbGlv",
"nm":"Portfolio",
"selected":"1a",
"options":[
{
"cd":"1a",
"val":"Option 1A"
}
]
},
{
"cd":"UHJvZHVjdCBOYW1l",
"formname":"sp_filter_UHJvZHVjdCBOYW1l",
"nm":"Product Name",
"selected":"2b",
"options":[
{
"cd":"2a",
"val":"Option 2A"
},
{
"cd":"2b",
"val":"Option 2B"
}
]
}
]
}
Here's the template HTML:
<form>
<div v-for="dropdown in dropdowns">
<div v-if="dropdown.availableToView">
<h4>{{dropdown.nm}}</h4>
<select v-model="dropdown.selected" v-on:change="triggerUpdate">
<option value="">(Make a selection)</option>
<option v-for="option in dropdown.options" :value="option.cd">{{option.val}}</option>
</select>
</div>
</div>
</form>
So far so good.
I've got the data loading and Vue is building the dropdowns.
When the user changes any select box (remember there can be an arbitrary number of them), the trigger action needs to submit ALL of the elements in the form via ajax. It sounds like the most correct option is to bind the form fields to the underlying component data, as I've done.
My triggerUpdate looks like this:
methods: {
triggerUpdate: function() {
axios({
method: "post",
url: actionURL,
data: this.dropdowns
})
.then(response => (this.data = response));
}
}
...but this submits the entire dropdowns data element, including all of the options in each select box. It's unnecessary to send all of the options in. I just want to send each field name along with its selected option (i.e. the "value").
I know i could serialize the whole form and make that my ajax payload. But that seems to be making an "end run" around Vue.js. Everyone talks about having your form fields bound to the Vue model...is it then correct to basically ignore the model when making an ajax request whose purpose is to then update the model?
I'm relatively new to Vue.js so I'd appreciate help with what I'm overlooking here. How should I go about sending in the data from the form (a) while using proper Vue.js binding and (b) without sending extraneous data?
Thanks for your time.
If you need to post only the selected values, and you store those in each dropdown's selected property, the sensible approach seems to be just mapping it to a simple array of name/value objects.
Try this (it assumes the name of each field is the formname property, if it isn't you can just replace it):
var submitData = this.dropdowns.map((dropdown) => {
return { name: dropdown.formname, value: dropdown.selected };
});
Then you send submitData in your ajax request.

Update select dynamically with API response data

I cannot get a select (drop-down menu) to update with a API response from ngfileupload (sheet names in the file uploaded). I can see the sheet names in what is returned in the browser but cannot get it "into" the select dropdown dynamically.
this.uploader.onSuccessItem=(item,response,status,headers)=>
{
if (response)
{
const res:UploadDataStep1=JSON.parse(response);
const returnDataStep1=
{
Url:res.Url,
SheetNames:res.SheetNames
};
console.log(returnDataStep1.Url);
// this.sheetNames=returnDataStep1.SheetNames;
this.makeTenderStep1_Form.controls['sheetNames'].patchValue(returnDataStep1.SheetNames);
//Input here what needs to happen with data
}
}
and HTML:
<select class="browser-default custom-select"
[ngClass]="{'is-invalid':makeTenderStep1_Form.get('sheetNames').errors&&
makeTenderStep1_Form.get('sheetNames').touched}"
formControlName="sheetNames" id="sheetNames">
<option *ngFor="let name of sheetNames; let i = index" [value]="sheetNames[i]">
{{name}}
</option>
</select>
<ng-template #loading>Awaiting data from uploaded file...</ng-template>
<!-- <span class="form-text text-muted">Please choose which sheet contains the relevant data (Mandatory)</span> -->
<div class="invalid-feedback">You need to select a sheet before continuing</div>
<div class="kt-heading kt-heading--md mt-3">c. Categorise data from file</div>
Found out it was a put request, so could not explicitly work with the return/response from the API (you actually can using Params, but thats a discussion for another day). Changed it to a get request instead which enabled me to work with the return data in the way i wanted.

How to add dynamically v-model and it's initial values to existed html elements by vuejs

For SEO purposes I need to render html elements by php. For example I have these elements.
<?php foreach ($elements as $key => $element): ?>
<select name="first" v-model="model[<?= $key; ?>]">
<option value="">Select</option>
<option value="1">Some Text</option>
<option value="2">Some Text</option>
</select>
<select name="second" v-model="model[<?= $key; ?>]>
<option value="">Select</option>
<option value="4">Some Text</option>
<option value="5">Some Text</option>
</select>
...
...
...
<select name="eleven" v-model="model[<?= $key; ?>]>
<option value="">Select</option>
<option value="101">Some Text</option>
<option value="102">Some Text</option>
</select>
<?php endforeach; ?>
And probably I can manipulate these elements like this on vue side.
const count_models = <?= count($elements) ?>; // in the html
const app = new Vue({
el: '#app',
data: {
model:[]
},
mounted(){
console.log(this.model);
for (let $i = 0; $i < count_models; $i++) {
this.model[$i] = "";
}
}
})
I cannot declare the initial values for model[?]. I need an xhr or assign counted items to a javascript variable to get how many select element I have on DOM and to declare initial values as null for each model[]. Even I redeclare the initial values of the models, it doesn't bind. I just put an example on jsFiddle. In Angular1 there was ng-init attribute to declare initial value for the model.
How can I solve this problem ?
https://jsfiddle.net/ks7jmgwv/1/
you just encountered one of the most common gotchas of Vuejs: reactivity (and therefor the lack off)!
The issue here is when the model property is created it's just an empty array/object and any property that you add to that element it's not going to be reactive: that means any kind of change made programmatically won't trigger the Vue's internal whatches, the only reason that the v-model still works is that the changes made by the user and not by the code does actually trigger native HTML events.
You have two possible solutions:
Ignore the reactivy part (but you won't be able to programmatically update the selected value, or at least it won't be visible) and just make sure that the 'Select' option will be selected by default by assigning it the correct value (in that way you can just skip all the default for-loop initialization).
<option :value="undefined" selected="selected" disabled>Select</option>
Follow the offical way suggested by the Vuejs' documentation to add a new property to an object and still having the advantage of reactivity
this.$set(this.model, $i, "");
You can check this plunker in which I'm showing you both the ways of obtaining your goal:
https://jsfiddle.net/8y7eu39z/18/
Also, did you know that for placeholder options in a select you can add the attribute disabled?
Reactivy Reference: https://v2.vuejs.org/v2/guide/reactivity.html
Also: if you want a "null" as a default value but did not manage to find a way to make it being recognized by the "select" options just use :value="null" instead of value="null" and then you should be able to use
this.$set(this.model, $i, null);

v-for : is there a way to get the key for the nested(second) loop besides "Object.keys(obj)[0]" in Vuejs?

Here's the markup:
<ul>
<li v-for="(topic,label,index) in guides" :key="index">
<ul>
<strong> {{label}} </strong>
<li v-for="rule in topic">
{{rule.val}},
{{Object.keys(topic)[0]}}
</li>
</ul>
</li>
And here's the data for this list:
data: {
guides: {
"CSS" : {
1502983185472 : {
"modifiedby" : "bkokot",
"val" : "When adding new rule, use classes instead of ID whenever possible"
},
1502983192513 : {
"modifiedby" : "bkokot",
"val" : "Some other rule"
},
},
"Other" : {
1502628612513 : {
"modifiedby" : "dleon",
"val" : "Some test text"
},
1502982934236 : {
"modifiedby" : "bkokot",
"val" : "Another version of text"
},
}
}
}
So as you can see there is a "guides" property which is an object of other objects that do have inner objects too.
All I want is to get the keys from inner (second) loop (numbers "1502983185472" etc).
The only solution that i see right now is "Object.keys(topic)[0]", but is there a more accurate alternative in vuejs for this?
Adding key, index parameters to the second loop(with new unique variable names) doesn't work for me.
Here's a working fiddle: https://jsfiddle.net/thyla/yeuahvkc/1/
Please share your thoughts.
If there is no good solution for this - may it be a nice topic for a feature request in Vuejs repo(unless I'm missing something terrible here)?
Generally if you're curious - that number is a momentjs timestamp - I'm using this data in firebase, and saving initial timestamp as an object key seemed to be a pretty nice solution (since we need a key anyway, to save some space - I can use this key instead of another extra timestamp property in my object, this also makes the instance very 'targetable' in firebase).
Thank you in advance ! Cheers!
p.s: another possible solution is converting inner loop (css, other) from objects into arrays and using time-stamp as another object property, but I'm using firebase - saving this data as an object gives me an ability to quickly access some instance without parsing the entire parent object/array, makes it more easy to filter, search, reupdate, etc - thus converting object into array is not a good solution for instance with very large number of items.
Your fiddle renders the number key of the first entry of a topic for each of the rules in that topic. I'm assuming you want to actually show the number key for each corresponding rule.
That value is passed as the second parameter in the v-for:
<li v-for="(rule, ruleID) in topic">
{{ rule.val }},
{{ ruleID }}
</li>
Here's a working fiddle.
Here's the documentation on using v-for with an object.
This can be solved by as follows in the second loop like
<li v-for="(rule,index) in topic">
{{rule.val}},
{{index}}
</li>
Please refer this fiddle => https://jsfiddle.net/yeuahvkc/7/
Use explicit definitions
Other answers use ".val", but it's not clear where that originates.
Instead, just declare everything, like:
<li v-for="(value, key, index) in rule">
{{key}}: {{value}} - {{index}}
</li>