How do I render options and optgroups conditionally with v-for? - javascript

I have a data structured like this
const options = [
{
group: 'Fruits', options: [
{ label: 'Apple', value: 'f-1' },
{ label: 'Banana', value: 'f-2' },
{ label: 'Orange', value: 'f-3' },
],
},
{ label: 'Chocolate', value: 'm-1' },
{ label: 'Cake', value: 'm-2' },
{
group: 'Vegetables', options: [
{ label: 'Cabbage', value: 'v-1' },
{ label: 'Tomato', value: 'v-2' },
],
},
{ label: 'Puddin', value: 'm-3' },
]
I would like to render it into a select component like this:
<select>
<optgroup label="Fruits">
<option value="f-1">Apple</option>
<option value="f-2">Banana</option>
<option value="f-3">Orange</option>
</optgroup>
<option value="m-1">Chocolate</option>
<option value="m-2">Cake</option>
<optgroup label="Vegetables">
<option value="v-1">Cabbage</option>
<option value="v-2">Tomato</option>
</optgroup>
<option value="m-3">Pudding</option>
</select>
I tried something like this but it gave me an error:
<select>
<optgroup v-for="group in options" v-if="group.group" :label="group.group" :key="group.group">
<option v-for="option in group" :key="option.value" :value="option.value">{{ option.label }}</option>
</optgroup>
<option v-for="option in options" v-if="!option.group" :key="option.value" :value="option.value">{{ option.label }}</option>
</select>
The 'options' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'
Any ideas on how should I render it correctly? I'm kinda struggling with this for a while, thanks in advance!

This is where the non-rendering <template> tag comes in handy
const options = [{"group":"Fruits","options":[{"label":"Apple","value":"f-1"},{"label":"Banana","value":"f-2"},{"label":"Orange","value":"f-3"}]},{"label":"Chocolate","value":"m-1"},{"label":"Cake","value":"m-2"},{"group":"Vegetables","options":[{"label":"Cabbage","value":"v-1"},{"label":"Tomato","value":"v-2"}]},{"label":"Puddin","value":"m-3"}]
new Vue({
el: "#app",
data: () => ({ options, selected: null })
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<select v-model="selected">
<template v-for="option in options">
<!-- if the `group` property is truthy -->
<optgroup v-if="option.group" :label="option.group" :key="option.group">
<option v-for="opt in option.options" :value="opt" :key="opt.value">
{{ opt.label }}
</option>
</optgroup>
<!-- otherwise -->
<option v-else :value="option" :key="option.value">
{{ option.label }}
</option>
</template>
</select>
<pre>selected = {{ selected }}</pre>
</div>
Note that you cannot put key attributes on <template> so those should go where appropriate on the elements within.
In the example above, I've also bound the entire option object instead of just the value but you can choose the best value to bind for your usage.

Related

Using Value of Option in select in Vue

In the following:
<select id="test">
<option value="1">Test One</option>
<option value="2">Test Two</option>
</select>
I want is, When i select the option, The value of selected option should appear in input box. Like when i click Test One, the input box should say 1 as selected
Is it possible to do this in Vue?
You have to used v-model to get the selected item from <select>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
</select>
<span>Selected: {{ selected }}</span>
In Javascript
new Vue({
el: '...',
data: {
selected: ''
}
})
I hope you got what you want!
You can accomplish this by using the #change event handler
<select #change="handleChange">
<option
v-for="item in options"
:value="item.value"
v-text="item.letter"
/>
</select>
new Vue({
el: "#app",
data: {
selected: undefined,
options: [
{ letter: 'A', value: '1' },
{ letter: 'B', value: '2' },
]
},
methods: {
handleChange({ target: { value } }) {
this.selected = value
}
},
})
Check out this fiddle

Vue.js dont re-render after select element change

using Vue.js.
I have two object arrays, category and categoryPar, category contains name and parent's name, categoryPar contains just name. I want to display only categories that belongs to selected parent. Trying to do it like this:
<select v-model="editing.categoryPar"">
<option v-for="cat in categoryPar" v-bind:value="cat.name">{{ cat.description }}</option>
</select>
<select v-model="editing.category">
<option v-for="cat in category" v-if="editing.categoryPar == cat.par_name" v-bind:value="cat.name">{{ cat.description }}</option>
</select>
Condition is fulfilled but not re-rendered. When I use in console vue.$forceUpdate(); then it works, till I change parent select.
Thank you for help.
You need to create a variable of your selected model first.
Currently you are giving your v-model an object, which is why it is not working
new Vue({
el: '#app',
data () {
return {
selectedCategory: '',
category: [{name: 'A', parent: 'Alpha'}, {name: 'B', parent: 'Bravo'}, {name: 'C', parent: 'Charlie'}],
categoryPar: [{name: 'A'}, {name: 'B'}, {name: 'C'}],
}
},
})
<script src="https://unpkg.com/vue#2.5.9/dist/vue.js"></script>
<div id="app">
<pre>Selected Cat: {{selectedCategory}}</pre>
<select name="" id="" v-model='selectedCategory'>
<option :value="cat.name" v-for="cat in category"> {{ cat.name }}</option>
</select>
<select name="" id="">
<option value="" v-for="cat in categoryPar" v-if="cat.name === selectedCategory"> {{ cat.name }}</option>
</select>
</div>

How can I set selected option selected in vue.js 2?

My component vue is like this :
<template>
<select class="form-control" v-model="selected" :required #change="changeLocation">
<option :selected>Choose Province</option>
<option v-for="option in options" v-bind:value="option.id" >{{ option.name }}</option>
</select>
</template>
I use this : <option :selected>Choose Province</option> to selected
But whene executed, on the gulp watch exist error
The error like this :
ERROR in
./~/vue-loader/lib/template-compiler.js?id=data-v-53777228!./~/vue-load
er/lib/selector.js?type=template&index=0!./resources/assets/js/components/bootst
rap/LocationBsSelect.vue Module build failed: SyntaxError: Unexpected
token (28:4)
Seems my step is wrong
How can I solve it?
Handling the errors
You are binding properties to nothing. :required in
<select class="form-control" v-model="selected" :required #change="changeLocation">
and :selected in
<option :selected>Choose Province</option>
If you set the code like so, your errors should be gone:
<template>
<select class="form-control" v-model="selected" :required #change="changeLocation">
<option>Choose Province</option>
<option v-for="option in options" v-bind:value="option.id" >{{ option.name }}</option>
</select>
</template>
Getting the select tags to have a default value
you would now need to have a data property called selected so that v-model works. So,
{
data () {
return {
selected: "Choose Province"
}
}
}
If that seems like too much work, you can also do it like:
<template>
<select class="form-control" :required="true" #change="changeLocation">
<option :selected="true">Choose Province</option>
<option v-for="option in options" v-bind:value="option.id" >{{ option.name }}</option>
</select>
</template>
When to use which method?
You can use the v-model approach if your default value depends on some data property.
You can go for the second method if your default selected value happens to be the first option.
You can also handle it programmatically by doing so:
<select class="form-control" :required="true">
<option
v-for="option in options"
v-bind:value="option.id"
:selected="option == '<the default value you want>'"
>{{ option }}</option>
</select>
The simplest answer is to set the selected option to true or false.
<option :selected="selectedDay === 1" value="1">1</option>
Where the data object is:
data() {
return {
selectedDay: '1',
// [1, 2, 3, ..., 31]
days: Array.from({ length: 31 }, (v, i) => i).slice(1)
}
}
This is an example to set the selected month day:
<select v-model="selectedDay" style="width:10%;">
<option v-for="day in days" :selected="selectedDay === day">{{ day }}</option>
</select>
On your data set:
{
data() {
selectedDay: 1,
// [1, 2, 3, ..., 31]
days: Array.from({ length: 31 }, (v, i) => i).slice(1)
},
mounted () {
let selectedDay = new Date();
this.selectedDay = selectedDay.getDate(); // Sets selectedDay to the today's number of the month
}
}
<select v-model="challan.warehouse_id">
<option value="">Select Warehouse</option>
<option v-for="warehouse in warehouses" v-bind:value="warehouse.id" >
{{ warehouse.name }}
</option>
Here "challan.warehouse_id" come from "challan" object you get from:
editChallan: function() {
let that = this;
axios.post('/api/challan_list/get_challan_data', {
challan_id: that.challan_id
})
.then(function (response) {
that.challan = response.data;
})
.catch(function (error) {
that.errors = error;
});
}
You simply need to remove v-bind (:) from selected and required attributes.
Like this :-
<template>
<select class="form-control" v-model="selected" required #change="changeLocation">
<option selected>Choose Province</option>
<option v-for="option in options" v-bind:value="option.id" >{{ option.name }}</option>
</select>
</template>
You are not binding anything to the vue instance through these attributes thats why it is giving error.
My code for reactive multiselect
data() {
return {
article: {title: 'aaaaa', 'categoriesIds': [1,3], ...},
selectCategories: {1: 'xxx', 2: 'www', 3: 'yyy', 4: 'zzz'},
}
},
template
<div class="form-group">
<label for="content">Categories</label>
<select name="categories"
v-model="article.categoriesIds"
id="categories"
multiple
class="form-control col-md-5"
size="6">
<option v-for="(category, key) in selectCategories"
:key="key"
v-bind:value="key">{{category}}</option>
</select>
</div>
Selected items are binded to the article.categoriesIds array.
Another way, which I often find more reliable, is you could add a directive to your app.js or wherever you are initiating your VueJS, eg:
Vue.directive('attr', (el, binding) => {
if (binding.value === true) binding.value = ''
if (binding.value === '' || binding.value) {
el.setAttribute(binding.arg, binding.value)
}
})
You can then utilise v-attr to set an attribute, eg:
<option value="Western Australia" v-attr:selected="form.state == 'Western Australia'">Western Australia</option>
So as I understand the main goal is to set "Choose Province" as the default. I tried other options but the simple one worked for me the best:
<template>
<select class="form-control" v-model="selected" :required #change="changeLocation">
<option>Choose Province</option> # just an option with no selected or assigned v-model
<option v-for="option in options" v-bind:value="option.id" >{{ option.name }}</option>
</select>
</template>

Vue.js <select> v-model not binding within component

I am trying Vue.js, everything looks great other than working with select inputs within components.
I created a basic fiddle setup to illustrate the problem :
https://jsfiddle.net/8f24xLdq/
<div class="panel-body" id="vue">
<example></example>
</div>
<script type="text/x-template" id="t">
<div>
<select v-bind="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<script>
Vue.component('example', {
template: "#t",
data: function() {
return {
selected: 'A',
options: [{
text: 'One',
value: 'A'
}, {
text: 'Two',
value: 'B'
}, {
text: 'Three',
value: 'C'
}]
}
}
});
new Vue({
el: "#vue"
});
</script>
Use v-model instead of v-bind in <select >
<select v-model="selected">
<div class="col-md-6">
<select v-model="selectedto">
<option v-for="option in estados" v-bind:value="option.id" :key="option.id" v-html="option.label">
</option>
</select>
<span v-html="selectedto"></span>
</div>
mounted: function () {
console.log('created', this);
var vm = this;
vm.$root.selectedto = 1;
}

"Sticky" select in Angular app

I have an annoying issue with an app that has an Angular-based frontend. A certain select box is "sticky" - you have to select an option twice to change to it. Here's a snippet which reproduces the issue:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script>
var app = angular.module('myapp', []);
app.controller('NewsCtrl', function($scope) {
// Set news data
$scope.news = {
specific_for_dealership: '003'
};
// Get dealers
$scope.dealers = [{
id: 1,
dealerid: '001',
name: 'Volvo'
}, {
id: 2,
dealerid: '002',
name: 'Saab'
}, {
id: 3,
dealerid: '003',
name: 'Seat'
}];
});
</script>
<div ng-app="myapp">
<div ng-controller="NewsCtrl">
<form>
<select name="specific_for_dealership" ng-model="news.specific_for_dealership">
<option value="">All</option>
<option ng-repeat="dealer in dealers" ng-selected="news.specific_for_dealership" value="{{ dealer.dealerid }}">{{ dealer.name }}</option>
</select>
</form>
</div>
</div>
Any idea what has gone wrong and how I might resolve this?
You do not need to use ng-selected to select your option, ng-model does that for you.
It is causing the model to get confused. Which is why you have to select it twice.
<select name="specific_for_dealership" ng-model="news.specific_for_dealership">
<option value="">All</option>
<option ng-repeat="dealer in dealers" value="{{ dealer.dealerid }}">{{ dealer.name }}</option>
</select>
Something else I would personally recommend as well is switching to ng-options to display your options list from the object. It will give you some more versatility. For example you can bind the whole object to the selector instead of just the ID.
<select name="specific_for_dealership"
ng-options="dealer.dealerid as dealer.name for dealer in dealers"
ng-model="news.specific_for_dealership">
<option value="">All</option>
</select>
Just set ng-selected="dealer.dealerid === news.specific_for_dealership"

Categories