Vue.js dont re-render after select element change - javascript

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>

Related

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

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.

How do i bind select box with input in vuejs?

I try to bind select box with input so I have a select box with pre defined options and when selected that option will be in the input and when the user types text in the input a dynamic new select option should be made if it is not in the pre defined list else it should match on one of the items.
<div class="col-md-2 text-center">
<select class="form-control" v-model="selected">
<option v-for="item in inventory" :value="item" :key="item.id">
#{{ item.name }}
</option>
</select>
<p>
#{{ selected.id}}
</p>
</div>
<input v-model="inputBind" placeholder="," type="text" class="form-control">
and
new Vue({
el:'#app',
data:{
inputBind:'',
inventory: [
{name: 'MacBook Air', id: 1},
{name: 'MacBook Pro', id: 2},
{name: 'Lenovo W530', id: 3},
{name: 'Acer Aspire One', id: 4}
],
selected: 2
},
created: function() {
this.selected = this.inventory.find(i => i.id === this.selected);
},
Use the the same data attr for the input, check js fiddle that way data is shared by both the inputs. Its is the easiest way. Note that for the select box to select the correct option now you must enter the matching value. Don't know what the reason is you need it this way but its not that user friendly.
Js
new Vue({
el: '#app',
data: {
selected: 'item1',
input: '',
items: {
1: {id: 1, val: 'item1'},
2: {id: 2, val: 'item2'},
3: {id: 3, val: 'item3'},
}
}
});
Html
<div id="app">
<select class="form-control" v-model="selected">
<option v-for="item in items" :value="item.val" :key="item.id">
{{ item.val }}
</option>
</select>
<p>
{{ selected }}
</p>
<input v-model="selected" placeholder="," type="text" />
</div>
You can bind to change and which is fired after the model value has been updated like so:
<select class="form-control" v-model="selected" #change="doSomethingWithChangedValue">
<option v-for="item in inventory" :value="item" :key="item.id">
#{{ item.name }}
</option>
</select>

angular select ng-selected not working (with <option ng-repeat>)

I've spent an hour and tried every conceivable permutation of properties to bind a select to a model as object, and ng-selected does nothing.
<select ng-model="localModel.priceType">
<option
ng-repeat="item in vm.priceTypes"
ng-selected="localModel.priceType == item"
ng-value="item"
>{{item.name}}</option>
</select>
or
<select ng-model="localModel.priceType">
<option
ng-repeat="item in vm.priceTypes"
ng-selected="localModel.priceType.id == item.id"
ng-value="item"
>{{item.name}}</option>
</select>
or
<select ng-model="localModel.priceType">
<option
ng-repeat="item in vm.priceTypes track by item.name"
ng-selected="localModel.priceType.name == item.name"
ng-value="item"
>{{item.name}}</option>
</select>
The select list renders correctly, option values look funky i.e "object:875". and selected does not work.
I need ng-model to be the object, not object.someId.
solved this by using ng-options instead of <option> ng-repat
<select ng-model="localModel.priceType" ng-options="item as item.namefor item in vm.priceTypes track by item.name"></select>
ngValue expects a string for the ngValue. To use ngRepeat with the <option> tag, then use value="{{item}}" instead of ng-value. Expand the snippet below to see it working.
angular.module('myApp', [])
.controller('ctrl', function($scope) {
$scope.vm = {
priceTypes: [{
id: 3,
name: 'pound'
},
{
id: 5,
name: 'Yen'
},
{
id: 6,
name: 'dollar'
}
]
};
//select model value
$scope.localModel = {
priceType: $scope.vm.priceTypes[1]
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ctrl">
<select ng-model="localModel.priceType">
<option
ng-repeat="item in vm.priceTypes as item"
ng-selected="localModel.priceType.id == item.id"
value="{{item}}"
>{{item.name}}</option>
</select>
<div>
priceType: {{ localModel.priceType }}
</div>
</div>
A simpler approach is to use ngOptions on the <select> tag, with a combination of forms:
select as label for value in array track by expr
Refer to the comprehension_expression forms in the Arguments section under Usage for more information.
angular.module('myApp', [])
.controller('ctrl', function($scope) {
$scope.vm = {
priceTypes: [{
id: 3,
name: 'pound'
},
{
id: 5,
name: 'Yen'
},
{
id: 6,
name: 'dollar'
}
]
};
//select model value
$scope.localModel = {
priceType: $scope.vm.priceTypes[1]
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ctrl">
<select ng-model="localModel.priceType" ng-options="item as item.name for item in vm.priceTypes track by item.name">
</select>
<div>
priceType: {{ localModel.priceType }}
</div>
</div>
https://docs.angularjs.org/api/ng/directive/ngSelected
ngSelected does not interact with the select and ngModel
directives, it only sets the selected attribute on the element. If you
are using ngModel on the select, you should not use ngSelected on the
options, as ngModel will set the select value and selected options.
Try
<select ng-model="localModel.priceType">
<option ng-repeat="item in vm.priceTypes track by item.name"
ng-value="item.name">
{{item.name}}
</option>
</select>
You can change ng-value to any value you want from vm.priceTypes[0].

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>

"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