v-list in v-list not working with vuetify - vue.js

I am trying to implement list in list using vuetify. But I didnt manage to make it work. Below is the code and sample data. Please help me make it work. I am using vuetify 2.3.10. If I comment out the part for the second list then the first list works
<template>
<div>
<v-toolbar id="filter">
<v-layout row wrap>
<v-flex xs12 md1>
<v-menu attach="#filter-users" offset-y>
<template v-slot:activator="{ on, attrs }">
<div class="dropdown" v-bind="attrs" v-on="on">User</div>
</template>
<v-list id="filter-users">
<v-list-item
v-for="search_user in search_users"
#click="filterUser({user: search_user})">
<v-list-item-title>{{ search_user.name }}</v-list-item-title>
</v-list-item>
<v-list v-if="search_user.sub_users.length">
<v-list-item v-for="(search_sub_users, index) in search_user.sub_users"
:key="`sub_user_${index}`"
#click="filterUsers({user: search_sub_user})">
<v-list-item-title>{{ search_sub_user.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-list>
</v-menu>
</v-flex>
</v-layout>
</v-toolbar>
</div>
</template>
search_users demo data
search_users:[{
name: 'James',
sub_users: [
{
name: 'Willy'
},
{
name: 'Jack'
},
}]
},
name: 'Rock',
sub_users: [
{
name: 'Randy'
},
{
name: 'Amy'
},
}]
],

There's a lot of things going on in your code so let me enumerate them for you:
On your second sub-list, it can't find the search_user variable since that variable is only accessible inside the <v-list-item/>, which is outside of the <v-list/>.
What you can do is to create a <template/> that will wrap both <v-list-item/> and <v-list/> then put the v-for there. Something like this:
<v-list ...>
<template v-for="(search_user, index) in search_users">
<v-list-item :key="`user_${index}`">...</v-list-item> <!-- User List -->
<v-list :key="`sub_user_${index}`">...</v-list> <!-- Sub User List -->
</template>
</v-list>
Also, don't forget the v-bind:key, or simply :key, to the v-for's child elements.
You attempt to attach the <v-menu/> to its default slot. The attach prop of <v-menu/> should be somewhere outside the <v-menu/> or inside the its activator slot. However, it should attach directly to the element in your activator slot by default so you can just omit the attach prop.
<v-menu offset-y> <!-- remove the `attach` prop -->
<template v-slot:activator="{ on, attrs }">
<div class="dropdown" v-bind="attrs" v-on="on">User</div>
</template>
<v-list>...</v-list>
</v-menu>
On your second sub-list, you mistakenly typed search_sub_user instead of search_sub_users:
v-for="(search_sub_users, index) in search_user.sub_users"` // wrong
v-for="(search_sub_user, index) in search_user.sub_users"` // right
Here is a refactored version of your code at codesandbox.

Related

Property or method "attrs" is not defined on the instance but referenced during render

i have this error when i press a button which its inside a v-tooltip, it should sends me to anothe page in web site:
<v-tooltip bottom mg="1">
<template v-slot:activator="{ on, attrs }">
<router-link
:to="{
name: 'trabajosObservaciones',
params: { datos: item },
}"
>
<v-btn
v-bind="attrs"
v-on="on"
color="orange"
fab
x-small
dark
class="ml-2"
>
<v-icon>mdi-briefcase-eye</v-icon>
</v-btn>
</router-link>
</template>
<span>Ver Trabajos</span>
</v-tooltip>
how can i solve it?
Error is not comming from this part of your code, it's in "src/app.vue"
probably using and unregistered data property in the template

vuetify v-list with reactive source collapses upon adding or removing items

not sure if the reactivity part is relevant, but I use vuetify with Meteor.js and my problem is that whenever the number of items in the sub-group changes, it collapses the entire list. That is extremely annoying as the list has two levels and I need to reopen both levels to get back to the group I am editing.
I use exactly the same structure as the official sub-group example:
https://vuetifyjs.com/en/components/lists/#sub-group
Say the number of items in the Admin section in that example above changes (which means reassigning a different array to a local variable in data(){}). Then the whole list will collapse.
Is there anything I can do to keep having opened the current item?
Thanks for any tips!
I tested [https://vuetifyjs.com/en/components/lists/#sub-group][1] in Vue 2 and for me, the menu didn't close when a new item was added.
I pushed a new item into data.admin
I assigned a totally new array to data.admin
In both cases, the menu remained open.
I think the question is, are you updating a component state or props.
Because updating component props would probably cause it to rerender and therefore close the menu.
Here is the code that worked for me:
<template>
<v-app>
<v-main>
<v-card class="mx-auto" width="300">
<v-list>
<v-list-group :value="true" prepend-icon="mdi-account-circle">
<template v-slot:activator>
<v-list-item-title>Users</v-list-item-title>
</template>
<v-list-group :value="true" no-action sub-group>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>Admin</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item v-for="([title, icon], i) in admins" :key="i" link>
<v-list-item-title v-text="title"></v-list-item-title>
<v-list-item-icon>
<v-icon v-text="icon"></v-icon>
</v-list-item-icon>
</v-list-item>
</v-list-group>
<v-list-group no-action sub-group>
<template v-slot:activator>
<v-list-item-content>
<v-list-item-title>Actions</v-list-item-title>
</v-list-item-content>
</template>
<v-list-item v-for="([title, icon], i) in cruds" :key="i" link>
<v-list-item-title v-text="title"></v-list-item-title>
<v-list-item-icon>
<v-icon v-text="icon"></v-icon>
</v-list-item-icon>
</v-list-item>
</v-list-group>
</v-list-group>
</v-list>
</v-card>
<v-btn #click="addElement">Add element</v-btn>
</v-main>
</v-app>
</template>
<script>
export default {
name: 'App',
data: () => ({
admins: [
['Management', 'mdi-account-multiple-outline'],
['Settings', 'mdi-cog-outline'],
],
cruds: [
['Create', 'mdi-plus-outline'],
['Read', 'mdi-file-outline'],
['Update', 'mdi-update'],
['Delete', 'mdi-delete'],
],
}),
methods: {
addElement() {
this.admins.push(['New', 'mdi-account-multiple-outline']);
// Completely change the menu
/* this.admins = [
['New', 'mdi-account-multiple-outline'],
['Management', 'mdi-account-multiple-outline'],
['Settings', 'mdi-cog-outline'],
]; */
},
},
};
</script>

displaying highlighted list items in vuetify 2.x

I'm writing my first app in vuetify 2.x. The app is displaying a list of items which are objects with a text (item.t) string field and a checked (item.c) boolean field.
I would like to display checked items in the current theme color and the unchecked items in the opposite theme color (highlighted). It thus depends on the value of the item.c field value.
I assume that changing the them of the list item will kind of reverse the colors of its content. Black <-> white.
How could I do that ?
This is my list component:
<template>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.r">
<v-list-item-content class="font-weight-medium">
<v-layout>
<v-row align="center">
<v-col cols="2">
<v-row no-gutters justify="end">
{{ item.n }}
</v-row>
</v-col>
<v-col cols="10" class="px-0">
<v-row no-gutters>{{ item.t }}</v-row>
</v-col>
</v-row>
</v-layout>
</v-list-item-content>
</v-list-item>
<v-divider
v-if="index < items.length - 1"
:key="`divider-${index}`"
></v-divider>
</template>
</v-list>
</template>
<script>
export default {
name: "itemList",
computed: {
items() {
return this.$store.getters.currentListItems;
},
},
};
</script>
I tried many things without success and couldn't find an example how to do that.
Edit: since the items contains just text and no icons, maybe it's enough the change the background and text color. The nice thing of theme is that it also reverse icons.
You can do it by different ways by v-menu by v-select or v-combo-box
and here you can use option multiple
https://vuetifyjs.com/en/components/combobox/#dense
but i think you need to use combo-box
and here you have slots
freedom of thought
I finally solved it. It's a bit hacky but it does the job.
The solution is to use a style computed with a method receiving the item object as argument. The hacky part is the way I change the style.
The highlighted text and background colors swap with the dark or light setting. Switching the them switches all the colors.
<template>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.r" v-bind:style="highlighted(item)">
<v-list-item-content class="font-weight-medium">
<v-layout>
<v-row align="center">
<v-col cols="2">
<v-row no-gutters justify="end">
{{ item.n }}
</v-row>
</v-col>
<v-col cols="9" class="px-0">
<v-row no-gutters>{{ item.t }}</v-row>
</v-col>
</v-row>
</v-layout>
</v-list-item-content>
</v-list-item>
<v-divider
v-if="index < items.length - 1"
:key="`divider-${index}`"
></v-divider>
</template>
</v-list>
</template>
<script>
export default {
name: "itemList",
computed: {
items() {
return this.$store.getters.currentListItems;
},
},
methods: {
highlighted(item) {
let backColor = "white";
let textColor = "#1E1E1E";
if (
(this.$vuetify.theme.dark && item.c) ||
(!this.$vuetify.theme.dark && !item.c)
) {
[textColor, backColor] = [backColor, textColor];
}
return {
"background-color": backColor,
color: textColor,
};
},
},
};
</script>

Vuetify - Prevent v-list-item-group De-selection

I have a v-list and v-list-item-group setup very similar to the one shown here in the Vuetify help:
https://vuetifyjs.com/en/components/lists/#flat
My problem comes if the user clicks on the same v-list-item twice - which then de-selects it without selecting another item.
I've tried mapping the v-model to a computed get(), set() and stopping the set, but this doesn't have any effect.
I really want the selected item to be set programmatically and I control it from an #click event.
Have you tried to use the mandatory property of v-list-item-group? It seems to do something close to what you want - the user cannot de-select an item, but can switch to another one. Something like this:
<v-card class="mx-auto" max-width="300" tile>
<v-list flat>
<v-subheader>REPORTS</v-subheader>
<v-list-item-group v-model="selectedItem" color="primary" mandatory>
<v-list-item v-for="(item, i) in items" :key="i">
<v-list-item-icon>
<v-icon v-text="item.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title v-text="item.text"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
First when user select one item value of selected Item Group is changed to new value and when double click still item is selected and active.
Something like this:
<v-list-item-group
v-model="selectedItemGroup"
:mandatory="selectedItemGroup!=-1"
>
</v-list-item-group>
<script>
export default {
data:function(){
selectedItemGroup:-1,
}
}
</script>

Meaning of v-slot:activator="{ on }"

Looking at the Vuetify example code for v-toolbar, what is the purpose of v-slot:activator="{ on }"? For example:
<template v-slot:activator="{ on }">
<v-toolbar-title v-on="on">
<span>All</span>
<v-icon dark>arrow_drop_down</v-icon>
</v-toolbar-title>
</template>
<script>
export default {
data: () => ({
items: [
'All', 'Family', 'Friends', 'Coworkers'
]
})
}
</script>
As far as I can see, on is not a defined variable anywhere, so I don't see how this is working. When I try it in my project, Internet Explorer throws an error on the <template v-slot:activator="{ on }">, but if I remove it, the page renders.
You're likely referring to this example:
<v-toolbar color="grey darken-1" dark>
<v-menu :nudge-width="100">
<template v-slot:activator="{ on }">
<v-toolbar-title v-on="on">
<span>All</span>
<v-icon dark>arrow_drop_down</v-icon>
</v-toolbar-title>
</template>
...
</v-menu>
</v-toolbar>
The following line declares a scoped slot named activator, and it is provided a scope object (from VMenu), which contains a property named on:
<template v-slot:activator="{ on }">
This uses destructuring syntax on the scope object, which IE does not support.
For IE, you'd have to dereference on from the scope object itself:
<template v-slot:activator="scope">
<v-toolbar-title v-on="scope.on">
But the ideal solution IMO is to use a Vue CLI generated project, which includes a Babel preset (#vue/babel-preset-app) to automatically include the transforms/polyfills needed for the target browsers. In this case, babel-plugin-transform-es2015-destructuring would be automatically applied during the build.
Details on the activator slot
VMenu allows users to specify a slotted template named activator, containing component(s) that activate/open the menu upon certain events (e.g., click). VMenu provides listeners for those events via an object, passed to the activator slot:
<v-menu>
<template v-slot:activator="scopeDataFromVMenu">
<!-- slot content goes here -->
</template>
</v-menu>
The slot content can access VMenu's event listeners like this:
<v-menu>
<template v-slot:activator="scopeDataFromVMenu">
<button v-on="scopeDataFromVMenu.on">Click</button>
</template>
</v-menu>
For improved readability, the scoped data can also be destructured in the template:
<!-- equivalent to above -->
<v-menu>
<template v-slot:activator="{ on }">
<button v-on="on">Click</button>
</template>
</v-menu>
The listeners from the scope object are passed to the <button> with v-on's object syntax, which binds one or more event/listener pairs to the element. For this value of on:
{
click: activatorClickHandler // activatorClickHandler is an internal VMenu mixin
}
...the button's click handler is bound to a VMenu method.
I think the original question is about understanding the "on" object. It is best explained here:
https://github.com/vuetifyjs/vuetify/issues/6866
Essentially "on" is a prop passed in from the activator. What v-on="on" does is bind that on prop to the component. "on" itself is all of the event listeners passed from the activator.
To call out a readability tip, it's possible to use this syntax:
<v-menu>
<template v-slot:activator="{ on: activationEvents }">
<v-btn v-on="activationEvents">
I like turtles 🐢
</v-btn>
</template>
</v-menu>
In my brain this has a more fluent readability than v-on="on", which to me is like observing a conversation consisting solely of:
Person 1: "Hey"
Person 2: "Yep"
Understand? ;)
By the way, activationEvents could be any alias, like "slotEvents", "listeners", "anyOldEvent", or whatever makes more sense to the reader as a renaming of the mysterious on.
Run the below code,you will know what is 'attrs' an 'on' in v-menu.
<v-menu>
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs" v-on="on">
v-menu slot activator:
<br />
attrs == {{ JSON.stringify(attrs) }}
<br />
on == {{ '{' + Object.keys(on).map(k => k + " : " + on[k]).join(',') + '}' }}
</div>
</template>
</v-menu>
Result:
v-menu slot activator:
attrs == {"role":"button","aria-haspopup":true,"aria-expanded":"false"}
on == {
click:function (e) {if (_this.openOnClick) {onClick && onClick(e);}_this.absoluteX = e.clientX;_this.absoluteY = e.clientY;},
keydown:function () { [native code] }
}
Explanation:
<div v-bind="attrs" v-on="on"> equals
<div
v-bind="{role:'button',aria-haspopup:true,aria-expanded:'false'}"
v-on="{click:function (e) {/*implement by v-menu*/},keydown:function () {/*implement by v-menu*/}}"
>
Starting in vue 2.4.0+, v-on also supports binding to an object of event/listener pairs without an argument. Note when using the object syntax, it does not support any modifiers.
Example:
<!-- v-on's object syntax (vue 2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
About <template> tags in Internet Explorer throws an error :
as vuetify docs say:
Template caveat
Due to Internet Explorer’s limited support for <template> tags, you must send fully compiled dom elements to the browser. This can be done by either building your Vue code in advance or by creating helper components to replace the dom elements. For instance, if sent directly to IE, this will fail:
<!-- Vue Component -->
<template v-slot:items="props">
<td>{‌{ props.item.name }‌}</td>
</template>