Vuejs how to use computed properties with a forEach loop - vue.js

My use case is something like this,
I loop through an array of objects to populate a drop-down menu.
If I v-model that drop down I can only get the object id or name. But I can get id and name both at the same time.
So I need computed property to find the id of the selected object.
This is my v-select
<v-select
label="Select an item"
:items="items"
item-text="name"
v-model="item_name">
/>
This is my computed property
computed: {
id() {
this.items.forEach(element => {
if (element.name == this.item_name) {
return (this.item = element.id);
}
});
}
}
What went wrong with my computed property I expected to {{item}} to print the id of the selected item but it didn't.

You may wan to use find() instead
computed: {
id() {
return this.items.find(element => {
return element.name == this.item.name
}).id;
}
}
This will first find the element in the Array that matches the condition in the function, and then the id of that element

The v-select component has return-object prop. Maybe try to use this to retrieve name and id at the same time, so the computed property will not be necessary at all.
Example:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
items: [{
id: 1,
name: 'test1'
},
{
id: 2,
name: 'test2'
},
{
id: 3,
name: 'test3'
},
],
item: null,
},
methods: {
onSelect() {
console.log(this.item);
},
},
});
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#6.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-select v-model="item" :items="items" item-text="name" return-object #change="onSelect"/>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>

Related

Vue.js v-autocomplete v-model new data

<v-autocomplete v-if="first.title == 'host'"
:items="host"
v-model="selected_host"
item-value="host_n"
outlined
hide-details
dense
></v-autocomplete>
I'd like to input new text which is not included in items.
autocomplete is just suggestion to user.
But I can't input new data in v-autocomplete. Whenever I write new data, it was deleted.
You should use v-combobox (doc here) instead of v-autocomplete if you want the user to add its own value
If you want to deal with multiple values, you can use the multiple attributes and passing an array to the v-model instead of a string (or an object if you use the return-object param)
new Vue({
el: '#app',
vuetify: new Vuetify(),
computed: {
tagsSorted() {
return this.tags.sort()
}
},
data: () => ({
host: [{
host_n: 'Foo',
},
{
host_n: 'Bar',
}
],
first: {
title: 'host'
},
selected_host: ''
}),
methods: {
sendData() {
console.log(this.selected_host)
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.4/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.4/dist/vuetify.min.css" />
<div id="app" data-app>
<v-combobox v-if="first.title == 'host'" :items="host" v-model="selected_host" item-value="host_n" item-text="host_n" outlined hide-details dense></v-combobox>
<v-btn #click="sendData">Send data</v-btn>
</div>

Vuetify Vuex v-for v-switch default true

I have a list of values in the Vuex store that I create v-switches for but I would like for the default value of the v-switch to be true on creation. I have tried input-value="true" but it does nothing. Any ideas how to set them to true since I can not find anything in the documentation that helps me?
<v-switch
v-for="(layerg, k) in getAddedGeoMetLayers"
:key="k"
:label="layerg"
:value="layerg"
dense
hide-details
v-model="selectedGeometLayers"
#change="updateSelectedAddedGeoMetLayers"
></v-switch>
export default {
mounted () {
this.selectedGeometLayers = this.getSelectedAddedGeometLayers
},
data () {
return {
selectedGeometLayers: []
}
},
computed: {
...mapGetters('map', [
'getSelectedAddedGeometLayers'
])
},
methods: {
updateSelectedAddedGeoMetLayers: function () {
this.$store.dispatch('map/updateSelectedAddedGeometLayers', this.selectedGeometLayers)
}
}
You need to set selectedValues Array to equal the getArray - this way all the v-switch will be selected at the beginning:
mounted() {
this.selectedValues = [...this.getArray]
}
From the docs, it looks like you can set the model to have every item in your array.
From: https://vuetifyjs.com/en/components/switches/#model-as-array
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
people: ['John', 'Jacob'],
}
},
})
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<p>{{ people }}</p>
<v-switch
v-model="people"
color="primary"
label="John"
value="John"
></v-switch>
<v-switch
v-model="people"
color="primary"
label="Jacob"
value="Jacob"
></v-switch>
</v-container>
</v-app>
</div>

How can I update an existing v-select by setting the v-model?

I have been teaching myself Vue (with limited success) but have run into the below issue.
I have two s each with a v-model:
Select 1:
<v-select
class="pr-2"
:items="latteSizeList"
v-model="sizeid"
item-text="sizename"
item-value="id"
#change="updateAvailibleOz(), calculate(), togglesizeSelected()"
solo
>
</v-select>
Select 2:
<v-select
:items="shotsArray"
v-model="shotcount"
:value.sync="shotcount"
item-text="shotsAmountName"
item-value="shotsAmountAmount"
#change="calculate()"
solo
>
</v-select>
I want to update the displayed value of the second v-select when the function togglesizeSelected() is called.
methods: {
togglesizeSelected: function () {
this.shotcount= this.sizeList.filter( (item) => item.id.indexOf(this.sizeid) > -1)[0].latteshots;
console.log("Shots Count: " + this.shotcount)
},
}
The variable is updating and is showing in the console. But I have not been able to get the displayed item to update. What am I missing?
Funny thing is that I met the same exact question today :)
(If the console hides the solution, just click "Full page")
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
sizeid: null,
latteSizeList: [{
id: 0,
sizename: "Small",
latteshots: 5,
},
{
id: 1,
sizename: "Large",
latteshots: 10,
},
],
shotcount: null,
shotsArray: [{
shotsAmountName: "5 shots",
shotsAmount: 5,
},
{
shotsAmountName: "10 shots",
shotsAmount: 10,
},
],
}
},
methods: {
latteSizeListClickHandler(e) {
this.togglesizeSelected(e)
this.calculate()
},
togglesizeSelected({
latteshots
}) {
this.shotcount = this.shotsArray.find(({
shotsAmount
}) => shotsAmount === latteshots)
},
calculate() {
console.log('calculate executed')
}
},
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
Select 1:
<v-select class="pr-2" :items="latteSizeList" v-model="sizeid" item-text="sizename" item-value="id" #change="(e) => latteSizeListClickHandler(e)" return-object solo>
</v-select>
Select 2:
<v-select :items="shotsArray" v-model="shotcount" item-text="shotsAmountName" item-value="shotsAmountAmount" solo>
</v-select>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
My idea:
Do not place multiple functions on a handler in the template. Create a "composition function" instead, and call that. (Generally, try to keep the template as simple in terms of JS code as possible. That's just good advice.)
You have to
set the v-select to return the whole object not the value only. You can do this by adding return-object to the v-select
pass in the object to the handler function as the argument ((e) => latteSizeListClickHandler(e) -> e is the argument)
set the shotcount to an object that's in the shotsArray, not only its shotsAmount
remove the :value.sync="shotcount" from the second v-select. .sync is for something else (that's a shorthand syntax for reacting to an event emitted in a child-component)
And voilá! The v-select works!
In a shorter way:
use return-object on the first v-select
find the object in shotsArray & set shotcount to that object
remove :value.sync - this messes up the v-model

Vuetify v-data-table custom filter for dropdown

I have a v-data-table that already has the :search and :sort-by enabled. I have now created a select where I pull in my status from VueX. Accepted, Rejected. What I want to do is not only search or sort but when selecting from the drop down, accepted only the accepted values display in the table.
<v-select
v-model="selectStatus"
label="Status"
:items="statusData"
item-value="id"
item-text="name"
return-object
#change="filterStatus"
/>
Is this the correct way to setup the filter?
methods: {
filterStatus () {
console.log('This is where I am planning to add my custom filter')
}
}
This is my statusData:
userStatus : [
{
id: 0,
name: "Accepted",
},
{
id: 1,
name: " Rejected",
},
];
Or better to pass in the data:
{ text: 'Status', value: 'status.name', filter: value => {}},
To disable certain values to be selected add a disabled property to your items.
var app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
items: [{
id: 0,
name: 'Accepted'
},
{
id: 1,
name: ' Rejected',
disabled: true
}
],
value: null
}
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-container>
Value : {{value}}
<v-select
v-model="value"
label="Status"
:items="items"
item-value="id"
item-text="name"
return-object
/>
</v-container>
</v-app>
</div>
Documentation : props-disabled

How to set an attribute on a v-select option?

Just starting with Vue and need some advice on best practices.
What I'm trying to achieve: set makeId equal to the makes option id, based on what option is selected. So, for example, when I select "bmw" from the dropdown, I want makeId to equal 2.
Without Vuetify, I cloud set a data attribute to each option during the v-for loop, and then #change just grab that attribute and assign the value to the makeid.
How can I achieve what I'm trying to do with Vuetify?
<v-select v-model="car.make" :items="makes" item-value="name" item-text="name"> </v-select>
data: function() {
return {
car: { make: "", makeId: "" },
makes: [{ name: "audi", id: "1" }, { name: "bmw", id: "2" }]
};
}
UPDATED:
you can bind the id as the item-value and on the v-model you need to assign car.madeIdinstead of car.make if you need a single value:
<v-select v-model="car.makeId" :items="makes" item-value="id" item-text="name"> </v-select>
Or you can use an extra method for object binding :
here is a fork for it: codepen
The main difficulty here is that you've got two different formats for your objects. Having to map between id/name and make/makeId complicates things.
This would be easy without the property mapping, you could just set return-object on the v-select.
Assuming that isn't possible, one alternative would be to have car as a computed property based on the selected value to perform the property renaming. This would look like this:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
selectedCar: null,
makes: [{name: 'audi', id: '1'}, {name: 'bmw', id: '2'}]
}
},
computed: {
car () {
const selectedCar = this.selectedCar
if (selectedCar) {
return {
make: selectedCar.name,
makeId: selectedCar.id
}
}
}
}
})
#app {
padding: 10px;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://unpkg.com/mdi#2.2.43/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://unpkg.com/vuetify#2.0.5/dist/vuetify.min.css" rel="stylesheet">
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.0.5/dist/vuetify.min.js"></script>
<div id="app">
<v-app>
<p>{{ car }}</p>
<v-select v-model="selectedCar" :items="makes" item-value="name" item-text="name" return-object> </v-select>
</v-app>
</div>
We could instead flip around the relationship between car and selectedCar so that car is in data and selectedCar is the computed property. For that we'd need to implement a getter and a setter:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
car: {make: '', makeId: ''},
makes: [{name: 'audi', id: '1'}, {name: 'bmw', id: '2'}]
}
},
computed: {
selectedCar: {
get () {
return this.makes.find(make => make.id === this.car.makeId)
},
set (selectedCar) {
this.car = {
make: selectedCar.name,
makeId: selectedCar.id
}
}
}
}
})
#app {
padding: 10px;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://unpkg.com/mdi#2.2.43/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://unpkg.com/vuetify#2.0.5/dist/vuetify.min.css" rel="stylesheet">
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.0.5/dist/vuetify.min.js"></script>
<div id="app">
<v-app>
<p>{{ car }}</p>
<v-select v-model="selectedCar" :items="makes" item-value="name" item-text="name" return-object> </v-select>
</v-app>
</div>
A third alternative is to ditch v-model altogether and just use the :value/#input pair directly:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
car: {make: '', makeId: ''},
makes: [{name: 'audi', id: '1'}, {name: 'bmw', id: '2'}]
}
},
methods: {
onInput (id) {
const make = this.makes.find(make => make.id === id)
this.car = {
make: make.name,
makeId: make.id
}
}
}
})
#app {
padding: 10px;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://unpkg.com/mdi#2.2.43/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://unpkg.com/vuetify#2.0.5/dist/vuetify.min.css" rel="stylesheet">
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.0.5/dist/vuetify.min.js"></script>
<div id="app">
<v-app>
<p>{{ car }}</p>
<v-select :value="car.makeId" :items="makes" item-value="id" item-text="name" #input="onInput"> </v-select>
</v-app>
</div>