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

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>

Related

Vue watch on objects

I'm using Vue2 and I have a problem to compare objects. But when compare "newVal" and "oldVal" values are always the same.
Child componenet:
name: "List",
components: {
Pagination, UserDetails
},
props: {
filters: Object
},
watch: {
filters: {
handler(val, oldVal) {
console.log(JSON.stringify(val));
console.log(JSON.stringify(oldVal));
},
deep: true,
}
},
Parent Componenet:
<template>
<div>
<b-form inline>
<label class="sr-only" for="inline-form-input-name">Name</label>
<b-form-input
id="inline-form-input-name"
class="mb-2 mr-sm-2 mb-sm-0"
v-model="filters.username"
placeholder="Name"
></b-form-input>
<label class="sr-only" for="inline-form-input-username">Email</label>
<b-input-group class="mb-2 mr-sm-2 mb-sm-0">
<b-form-input
id="inline-form-input-username"
v-model="filters.email"
placeholder="Email"></b-form-input>
</b-input-group>
<b-button variant="danger">Clear</b-button>
</b-form>
<List :filters="filters"/>
</div>
</template>
<script>
import List from "./List";
export default {
name: "AdminUserList",
components: {
List,
},
data() {
return {
filters: {
username: '',
email: ''
},
}
},
}
</script>
<style lang="scss">
</style>
Result:
{"username":"d","email":""}
{"username":"d","email":""}
from the official doc you will find:
Note: when mutating (rather than replacing) an Object or an Array, the old value will be the same as new value because they reference the same Object/Array. Vue doesn’t keep a copy of the pre-mutate value.
vue js watcher DOC
one thing that you can do is to watch the object's property directly like:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
filters: {
name: '',
email: '',
},
};
},
watch: {
'filters.name' (newVal, oldVal) {
console.log(`new name: ${newVal}`, `, old name: ${oldVal}`);
},
'filters.email' (newVal, oldVal) {
console.log(`new email: ${newVal}`, `, old email: ${oldVal}`);
},
},
});
<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">
<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>
<div id="app">
<v-app>
<v-container>
<v-row class="px-5">
<v-text-field v-model="filters.name" outlined label="name"></v-text-field>
</v-row>
<v-row class="px-5">
<v-text-field v-model="filters.email" outlined label="email"></v-text-field>
</v-row>
</v-container>
</v-app>
</div>
Try to adjust watcher accordingly
watch: {
filters: {
handler: function(val, oldVal) {
console.log(JSON.stringify(val));
console.log(JSON.stringify(oldVal));
},
deep: true,
}
},
I have just followed documentation from the official website

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

vuetify v-alert incorrectly dismissing sibling when transition is applied

I am using vuetify v-alert with v-for=alert in alerts looping through alerts array, when an alert is dismissed #input event is fired and I'm removing it from alerts array.
The problem I am facing, that when the element is clicked, transition is applied, and the sibling alert is now shown at the position where the one which was dismissed, seems like the click event is fired for the sibling element as well and the sibling v-alert is isVisible == false but the #input event is not fired.
If I remove transition="scroll-y-reverse-transition" from the v-alert it works properly.
Why is this happening?
const alertsComponent = {
name: "MyAlertsComponent",
template: "#alerts",
data() {
return {
alerts: []
};
},
created() {
this.$root.$off("alert");
this.$root.$on("alert", this.addAlert);
},
methods: {
closeAlert(idx, alert) {
console.log(`deleting alert idx: ${idx} - ${alert}`);
this.$delete(this.alerts, idx);
},
addAlert(alert, ...args) {
alert.type = alert.type || "error";
this.alerts.unshift(alert);
}
}
};
const app = new Vue({
el: "#app",
vuetify: new Vuetify(),
components: {
alertsComponent
},
mounted() {
[...Array(8).keys()].forEach((e) => {
this.fireAlert(this.counter++);
});
},
methods: {
fireAlert(val = this.counter++) {
const alert = this.generateAlert(val);
this.$root.$emit("alert", alert);
},
generateAlert(val, type = "error") {
return {
val,
type
};
}
},
data() {
return {
counter: 1
};
}
});
.alert-section {
max-height: 400px;
padding: 5px;
}
<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>
<header>VueTify!</header>
<hr>
<v-row>
<v-col>
<v-btn #click="fireAlert()">Add Alert</v-btn>
</v-col>
</v-row>
<alerts-component>Hi</alerts-component>
</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>
<template id="alerts">
<div>
<div class="alert-section overflow-y-auto">
<v-alert v-for="(alert, index) in alerts" :key="index" dense dismissible elevation="5" text :type="alert.type" #input="closeAlert(index, alert)" #addAlert="addAlert"
transition="scroll-y-reverse-transition"
>
{{ alert.val }}
</v-alert>
</div>
<hr>
<pre class="code">{{ JSON.stringify(alerts, null, 2) }}
</pre>
</div>
</template>
Looks like the problem is using the index as key.
If I change :key="alert.val" it works fine.

Vuejs how to use computed properties with a forEach loop

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>

How to load Array Data into Vuetify Select Input

I'm trying to show "locations" in a vuetify select component, but my current code renders "[object Object]" instead of Location 1, Location 2, etc.
My select component:
<v-select
:items="locations"
v-model="location"
label="Choose Location"
bottom
autocomplete
></v-select>
Locations:
locations () {
return this.$store.getters.getLocationsForEvent(this.event.id)
}
Vuex Getter:
getLocationsForEvent: (state) => (id) => {
return state.loadedLocations.filter(function (location) {
return location.eventId === id;
});
}
Here is a screenshot of what the location data looks like:
Thanks!
For custom objects you have to specify the item-text. The item-text is what each option will display.
From your screenshot, for instance, title is a possible property:
<v-select
:items="locations"
v-model="location"
label="Choose Location"
item-text="title"
bottom
autocomplete
></v-select>
Demos below.
Without item-text:
new Vue({
el: '#app',
data () {
return {
location: null,
locations: [
{ id: "1111", manager: 'Alice', title: 'Water Cart 1' },
{ id: "2222", manager: 'Bob', title: 'Water Cart 2' },
{ id: "3333", manager: 'Neysa', title: 'Water Cart 3' }
]
}
}
})
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'>
<link rel='stylesheet' href='https://unpkg.com/vuetify#1.0.10/dist/vuetify.min.css'>
<script src='https://unpkg.com/vue/dist/vue.js'></script>
<script src='https://unpkg.com/vuetify#1.0.10/dist/vuetify.min.js'></script>
<div id="app">
<v-app>
<v-container>
<v-select
:items="locations"
v-model="location"
label="Choose Location"
bottom
autocomplete
>
</v-select>
</v-container>
</v-app>
</div>
With item-text:
new Vue({
el: '#app',
data () {
return {
location: null,
locations: [
{ id: "1111", manager: 'Alice', title: 'Water Cart 1' },
{ id: "2222", manager: 'Bob', title: 'Water Cart 2' },
{ id: "3333", manager: 'Neysa', title: 'Water Cart 3' }
]
}
}
})
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'>
<link rel='stylesheet' href='https://unpkg.com/vuetify#1.0.10/dist/vuetify.min.css'>
<script src='https://unpkg.com/vue/dist/vue.js'></script>
<script src='https://unpkg.com/vuetify#1.0.10/dist/vuetify.min.js'></script>
<div id="app">
<v-app>
<v-container>
<v-select
:items="locations"
v-model="location"
label="Choose Location"
item-text="title"
bottom
autocomplete
>
</v-select>
</v-container>
</v-app>
</div>
implemeneted a watch to have a low level Array of objects
watch: {
groupInfo: function(groupInfo) {
if (groupInfo.teams !== undefined) {
var newArray = [];
for (var key in groupInfo.teams) {
var obj = groupInfo.teams[key];
newArray.push(obj);
}
console.log("wagwan" newArray)
this.teams = newArray;
}
}
},