I am using the vuetify framework and I am running into this issue where I am not sure how I can add an item from the list multiple times. I have a dropdown list and I would like to add the option foo or any option multiple times on select. Here is a link to the demo codepen.
So right now if I select foo or any other option and then select it again from the dropdown list, it goes away, instead I want another chip with same option
added into it?
new Vue({
el: '#app',
data() {
return {
items: [{
text: 'Foo',
value: 'foo'
},
{
text: 'Bar',
value: 'bar'
},
{
text: 'biz',
value: 'buzz'
},
{
text: 'buzz',
value: 'buzz'
}
],
}
}
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox :items="items" label="Add Multiple Chips" multiple small-chips solo deletable-chips>
<template v-slot:item="{ index, item }">
<v-list-tile-content>
{{item.text}}
</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info">
{{ item.text }}
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
If anyone has any clue on how to achieve this. It will be much appreciated. Thank you
A couple of small adjustments,
put a .stop on the item click to prevent Vuetify from processing after your handler
tell the combo-box to use arr for :value
add a delete click handler to v-chip and corresponding method (NB this works on Vuetify 2.1.0, but not on Vuetify 1.5.14 as used on the Codepen. If you don't need that specific version, install the latest.
Codepen Vuetify v1.5.14
CodeSandbox Vuetify v2.1.0
<template>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox
:items="items"
label="Add Multiple Chips"
multiple
small-chips
solo
deletable-chips
:value="arr"
>
<template v-slot:item="{ index, item }">
<v-list-tile-content #click.stop="multipleSelection(item)">{{item.text}}</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info"
#click:close="deleteChip(item)" >{{ item.text }}</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
</template>
<script>
export default {
name: "playground",
data: () => ({
arr: [],
items: [
{
text: "Foo",
value: "foo"
},
{
text: "Bar",
value: "bar"
},
{
text: "biz",
value: "buzz"
},
{
text: "buzz",
value: "buzz"
}
]
}),
methods: {
multipleSelection(item) {
this.arr.push({...item});
console.log(this.arr);
},
deleteChip(item) {
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr);
}
}
};
</script>
Addressing the problem of long selection lists being obscured by the dropdown menu,
The dropdown menu looks like this at runtime (via chrome developer tools)
<div class="v-menu__content theme--light menuable__content__active v-autocomplete__content"
style="max-height: 304px; min-width: 357px; top: 149px; left: 12px; transform-origin: left top; z-index: 8;">
<div role="listbox" tabindex="-1" class="v-list v-select-list v-sheet v-sheet--tile theme--light theme--light"
id="list-261">
<div tabindex="0" role="menuitem" id="list-item-267" class="v-list-item v-list-item--link theme--light">Foo</div>
<div tabindex="0" role="menuitem" id="list-item-268" class="v-list-item v-list-item--link theme--light">Bar</div>
<div tabindex="0" role="menuitem" id="list-item-269" class="v-list-item v-list-item--link theme--light">biz</div>
</div>
</div>
and has these styles
element.style {
max-height: 304px;
min-width: 357px;
top: 149px;
left: 12px;
transform-origin: left top;
z-index: 8;
}
Vuetify changes the top: 149px in it's selection handler, but since we turned that off we need to call updateMenuDimensions() in our own handler multipleSelection().
To do this, add a ref to the combobox,
<v-combobox
:items="items"
label="Add Multiple Chips"
multiple
small-chips
solo
deletable-chips
:value="arr"
ref="combobox"
>
...
</v-combobox>
Then add the call to updateMenuDimensions, inside a nextTick() to allow the selection box to settle.
methods: {
multipleSelection(item) {
this.arr.push({ ...item });
console.log(this.arr);
this.$nextTick(() => this.$refs.combobox.updateMenuDimensions())
},
deleteChip(item) {
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr);
this.$nextTick(() => this.$refs.combobox.updateMenuDimensions())
}
}
Codepen Vuetify v1.5.14 (NB not deleting chips).
Preventing text from adding an item
There's another problem (for anyone following here but not seeing the chat room discussion). I've separated the answers for each issue, to make things easier to follow.
Feel free to vote on all answers :).
Problem
If the user types into the combobox, the expectation is that the list will be filtered, and it is.
But if the user presses enter after the text, an item is added and this is not desired.
We tried a permutations of event suppression, including,
#keydown.enter.prevent
#keypress.enter.prevent
#submit.prevent
#keydown.enter.prevent="enterItem" with event.preventDefault() in the handler
#blur.prevent
plus permutations with .stop, .capture.
None of the event suppression worked, so a hacky way to do it is to v-if the v-chip
<v-combobox
...various attributes as before
>
<template v-slot:item="{ index, item }">
...
</template>
<template v-slot:selection="{ index, item }">
<v-chip v-if="item.text" ...other attributes >{{ item.text }}</v-chip>
</template>
</v-combobox>
This works because the added item has no text (reason unknown), and the variable this.arr does not get modified when pressing the enter key as described.
Codepen
Related
I am more of a Vue-Noob, and find it to be a bit complicated to understand. Maybe you fellas see that code, and have the "best practice" or most compact solution for my problem:
I have an autocomplete dropdown box. On expand, i see a list with clickable entries. I would need to get the information of the hovered item, if the mouse hovers over the list.
Like, the method "doFunnyStuff" would push the data of the hovered object on hovering to a method, and do something with it (maybe externally or so)
Thank you in advance for any helpful comment!
<template>
<v-autocomplete
v-model="selected"
dense
outlined
hide-details
return-object
background-color="white"
light
:placeholder="placeholder"
hide-no-data
style="width: 500px"
:loading="loading"
:search-input.sync="query"
:items="items"
clearable
>
<template slot="selection" slot-scope="{ item }" return-object>
<v-list-item #mouseenter="doFunnyStuff(item)">
<v-list-item-content>
<v-list-item-title> {{ item.text }}} </v-list-item-title>
</v-list-item-content>
</v-list-item>
</template></v-autocomplete
>
</template>
</template>
Maybe I misunderstood your question, but when you want to do something with hovered item of expanded list, you should use item slot instead of selection.
Selection slot is used just for your selected item on top of the component, so your code works, but only when you hover your already selected item.
Mouseenter event is not related for any component in vuetify, it's an JS event that may be appended to any component (if was not appended before by vuetify or any different library, ofc).
So your code may be like:
<div id="app">
<v-app id="inspire">
<v-autocomplete
v-model="value"
:items="items"
dense
filled
label="Filled"
>
<template #item="{ item, on, attrs }">
<v-list-item #mouseenter="doFunnyStuff(item)" v-on="on" v-bind="attrs">
<v-list-item-content>
<v-list-item-title> {{ item }} </v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-autocomplete>
<div>
<p>{{ hoveredItem }}</p>
</div>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: ['foo', 'bar', 'fizz', 'buzz'],
value: null,
hoveredItem: "not hovered yet"
}),
methods: {
doFunnyStuff (item) {
this.hoveredItem = item;
}
}
})
Codepen link with an example
I would like to have responsive drawer, so tried to code in accordance with Quasar Docs, however, it was not resized properly when the screen size is being changed. In order to solve this, I used computed in the script. I think due to this computed, the function drawerClick not working.
When the drawer is on minimode, and one of menus are clicked, then the menu should be expanded, but the problem I have is it never expanded.
template
<template>
<q-drawer
v-model="drawer"
show-if-above
:mini="!drawer || miniState || miniStatus"
#click.capture="drawerClick"
:width="220"
:breakpoint="500"
bordered
:content-style="{ backgroundColor: '#f5f7f9' }"
id="side-menu"
>
<q-scroll-area class="fit">
<q-list padding>
<q-btn v-if="!miniState" flat left class="logo-btn">
<img src="~assets/os_logo.png" width="144px" height="24px" contain />
</q-btn>
<q-btn v-else flat left>
<img src="~assets/os_logo_no_text.png" width="24px" contain />
</q-btn>
<q-expansion-item
default-opened
v-for="(menu, index) in menus"
header-class="header-bg text-black"
expand-icon-class="text-gray"
>
<q-expansion-item
v-for="(sub, index) in menu.subMenus"
:key="index"
:label="sub.title"
expand-icon="none"
class="sub-content"
:to="{ name: sub.link }"
/>
</q-expansion-item>
</q-list>
</q-scroll-area>
</q-drawer>
</template>
script
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'SideMenu',
data() {
return {
drawer: false,
miniStatus: false,
switchToMini: 700
};
},
computed: {
miniState: function() {
return this.$q.screen.width < this.switchToMini;
}
},
methods: {
drawerClick(e) {
if (this.miniStatus) {
this.miniStatus = false;
e.stopPropagation();
}
}
}
});
</script>
I explained better here in this image
[1]: https://i.stack.imgur.com/PqYoc.png
I'm new to Vue.js and Vuetify
Problem - To show tooltip on each dropdown item in v-autocomplete
Solution - added v-tooltip component in item template
Code:
var app = new Vue({
el: "#app",
data: {
items: [{
value: 0,
text: "Matthews Webb"
},
{
value: 1,
text: "Teresa Ward"
},
{
value: 2,
text: "Cervantes Swanson"
},
{
value: 3,
text: "Helga Cooper"
},
{
value: 4,
text: "Solomon Jensen"
},
{
value: 5,
text: "Eliza Delgado"
},
{
value: 6,
text: "Dickson Parks"
},
{
value: 7,
text: "Etta Glenn"
},
{
value: 8,
text: "Alma Durham"
},
{
value: 9,
text: "Rosemary Conner"
}
],
selected: []
}
});
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-content>
<v-container>
<v-autocomplete :items="items" v-model="selected" clearable multiple>
<template v-slot:item="data">
<v-tooltip right>
<template slot="activator" slot-scope="{ on }">
<v-list-tile-action>
<v-checkbox v-model="selected" :value="data.item.value"></v-checkbox>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title v-html="data.item.text" v-on="on"></v-list-tile-title>
</v-list-tile-content>
</template>
<span>{{ data.item.text }}</span>
</v-tooltip>
</template>
</v-autocomplete>
</v-container>
</v-content>
</v-app>
</div>
Problems after implementing the solutions:
Checkboxes are not clickable or item cannot be selected if clicked on the checkbox
On searching the item some of the checkboxes shows selected
Vue Version - 2.6.12
Vuetify Version - 1.5.24
Is there any other way to show tooltip on autocomplete dropdown items without disturbing the functionality or is there any mistake in my solution
Solution:
The v-input--selection-controls__ripple Div overlaps the input box of the selection control; hence binds to the click instead.
To fix this I applied a simple CSS Hack. Conceptually position it above by using position and z-index:-10 for the div.
that is illustrated with edits marked below as /* <--------------- */
<div class="v-input__slot">
<div class="v-input--selection-controls__input">
<input aria-checked="false" role="checkbox" type="checkbox" value="1" style="
position: relative; /* <--------------- */
">
<div class="v-input--selection-controls__ripple" style="
position: absolute; z-index: -10; /* <--------------- */
"></div>
<i aria-hidden="true" class="v-icon material-icons theme--light">check_box_outline_blank</i>
</div>
</div>
I will create a menu with jump marks for accessibility that is only visible when the tab key on the keyboard is clicked.
How can I implement this in Vuetify? is there a way to use something like #click for this?
This is my html code for the menu:
<template>
<div class="m-block-tab-jump-sections" data-module="tab-jump-sections" v-on:click.tab="onClick">
<div class="jump-sections js-sections h-break-in">
<a href="#tab-jump-section--metamenu" class="jump-link" title="" target="" tabindex="50">
zur Top-Navigation
</a>
</div>
</div>
</template>
In order to capture the tab on the entire page, you may need to look at putting the following on the app element:
v-on:keydown.tab='handleTab'
Now you can open a menu or do other actions in the handler.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
showMenu: false,
clickcount: 0,
items: [
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me 2' },
],
}),
methods: {
handleTab(event) {
this.showMenu = true;
}
}
})
<div id="app" v-on:keydown.tab='handleTab'>
<v-app id="inspire">
<div class="text-center">
<v-menu offset-y v-model='showMenu'>
<template v-slot:activator="{ on }">
<v-btn
color="primary"
dark
v-on:focus='handleTab'
>
Dropdown
</v-btn>
</template>
<v-list>
<v-list-item
v-for="(item, index) in items"
:key="index"
#click=""
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
<v-text-field></v-text-field>
<v-text-field></v-text-field>
<v-text-field></v-text-field>
<v-text-field></v-text-field>
</v-app>
</div>
Here is a sample:
I am trying to build a navigation which staggers the appearance of the contained list items when the menu/navigation is shown.
I have a burger symbol, and when clicked it renders the navigation (fullscreen).
I would now like to have an animation, where the different list items (actual links) appear with some delay to each other, the top one being the first and the bottom one the last.
I thought I could do this with vue's <transition-group> and list transitions, but all the examples are about adding and removing list items, whereas I have all of them from the beginning.
I then read about this: Vue.js: Staggering List Transitions
And I thought that might be it.
Unfortunately I could not get it to work either.
Do you have any hints how I could do that?
So far, I render the navigation with v-if:
<transition name="u-anim-fade" mode="in-out">
<Navigation v-if="navMenuOpen" />
</transition>
Within the Navigation component:
<nav>
<ul class="Navigation__list">
<li
v-for="(item, key) in menu.items"
class="Navigation__item"
:key="`nav-item-${key}`">
<nuxt-link>
<span v-html="item.title" />
</nuxt-link>
</ul>
</nav>
(I left out some stuff simplify the code here)
When I add the enter/leave/onEnter functions which are suggested here: Vue.js: Staggering List Transitions
v-bind:css="false"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
Like so:
<nav>
<transition-group
name="staggered-fade"
tag="ul"
class="Navigation__list"
v-bind:css="false"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
>
<li
v-for="(item, key) in menu.items"
class="Navigation__item"
:key="`nav-item-${key}`">
<nuxt-link>
<span v-html="item.title" />
</nuxt-link>
</transition-group>
</nav>
The methods (which I add to the methods of course) would not even be executed when I render the Navigation component. Probably because the items are not added or removed.
Probably because the items are not added or removed.
You're right, and what you need is this:
Transitions on Initial Render
If you also want to apply a transition on the initial render of a
node, you can add the appear attribute:
<transition appear>
<!-- ... -->
</transition>
I just tried it and if not present, the listener functions aren't called on the initial render.
Note that it works as well with <transition-group> components.
For vue 3 user, with GSAP
<template>
<div>
<transition-group
appear
tag="ul"
#before-enter="beforeEnter"
#enter="enter"
>
<li v-for="(item, index) in items" :key="icon.name" :data-index="index">
<div>{{ item.text }}</div>
</li>
</transition-group>
</div>
</template>
<script>
import { ref } from 'vue'
import { gsap } from 'gsap'
export default {
setup() {
const items = ref([
{ text: 'by email' },
{ text: 'by phone' },
])
const beforeEnter = (el) => {
el.style.opacity = 0
el.style.transform = 'translateY(100px)'
}
const enter = (el, done) => {
gsap.to(el, {
opacity: 1,
y: 0,
duration: 0.8,
onComplete: done,
delay: el.dataset.index * 0.2
})
}
return { items, beforeEnter, enter }
}
}
</script>
More info here