Is it possible to create a grid layout in a list? - vue.js

disclaimer: I'm just learning vue.js and vuetify and I'm not very familiar with javascript. I'm writing my first vue.js app.
I'm using vue 2.x and vuetify 2.x.
I need to create a scrolable list of items on which the user will be able to perform actions. For now, I'm just concerned about the layout of the list items.
The item is made of two visible information, a number and a text (like an inventory) that should be displayed in two columns. The numbers should be right aligned in their column and the text left aligned.
This is what I have so far.
<template>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.r">
<v-list-item-content class="font-weight-bold">
<v-list-item-title>
{{ item.n }} {{ item.t }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-divider :key="index"></v-divider>
</template>
</v-list>
</template>
<script>
export default {
name: "itemList",
data() {
return {
items: this.$store.state.currentItems,
};
},
};
</script>
Is it possible to use a layout inside a list item ? If not, how could I create the desired column alignment ?
In case it could influence the implementation, the actions are
make the item a sliding button with multiple selectable actions
or make the number and the text individual clickable flat buttons with popup menus
manually reorder list items with drag and drop.
An alternative to the sliding button (if not possible) is to make the number and text flat buttons with a popup menu. I guess this could influence how to define the layout.

generally with the use of v-row and v-col you can implement a layout and this is true inside of a v-list-item-content, for example check the code below (I'm not sure if I understood the layout you've asked for correctly or not but it must give you a hint)
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
items: [{
t: 'title one',
n: 1
},
{
t: 'title two',
n: 2
},
{
t: 'title three',
n: 3
},
{
t: 'title four',
n: 4
},
],
}
})
<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>
<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>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.n">
<v-list-item-content class="font-weight-bold">
<v-row>
<v-col cols="6">
<v-row no-gutters justify="end">{{ item.n }}</v-row>
</v-col>
<v-col cols="6">
<v-row no-gutters>{{ item.t }}</v-row>
</v-col>
</v-row>
</v-list-item-content>
</v-list-item>
<v-divider :key="`divider-${item.n}`"></v-divider>
</template>
</v-list>
</v-container>
</v-main>
</v-app>
</div>

Related

Vuetify - combobox badges with a removable button

I have a combobox:
<v-combobox
v-model="selectedCategories"
:items="attributeCategories"
item-text="name"
item-value="id"
label="Category"
multiple
chips
v-on:blur="changeCategory(selectedCategories)"
></v-combobox>
It's rendered like this:
I would like to add an x on each badge, so I can quickly remove it.
Is there a setting that I need to turn on?
In regular input type text, I can just add these props
clear-icon="mdi-close-circle"
clearable
The VComboBox's selection slot enables customizing the rendering of the selections. You could add a VChip with a VIcon child that re-selects the item when clicked (thus toggling its selection, and removing the chip):
<v-combobox ⋯ chips>
<template v-slot:selection="{ attrs, item, parent, selected }">
<v-chip v-bind="attrs" :input-value="selected">
<span class="pr-2">
{{ item.name }}
</span>
<v-icon small #click="parent.selectItem(item)"> $delete </v-icon>
</v-chip>
</template>
</v-combobox>
demo
You can simply achieve that by adding the <v-icon> inside <v-chip> in the <v-slot> template. I just created the working demo with the same data set you have in the screenshot mentioned above.
Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: ['Weather', 'Geo Location', 'Device', 'Date and Time', 'Product'],
model: ['Weather', 'Geo Location', 'Device', 'Date and Time', 'Product']
})
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.5/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.5/dist/vuetify.min.css"/>
<div id="app">
<link rel="stylesheet" href="https://unpkg.com/#mdi/font#6.x/css/materialdesignicons.min.css"/>
<v-app id="inspire">
<v-container fluid>
<v-combobox
v-model="model"
:items="items"
label="Category"
multiple
small-chips
>
<template v-slot:selection="{ item, parent }">
<v-chip
label
small
>
<span class="pr-2">
{{ item }}
</span>
<v-icon
small
#click="parent.selectItem(item)"
>
$delete
</v-icon>
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
There is a chips props your <v-combobox...> - so depends on Vuetify version there are two props you should add:
deletable-chips for Vuetify 2 according to docs
<v-combobox
...
chips
deletable-chips
/>
closable-chips for Vuetify 3 according to docs
<v-combobox
...
chips
closable-chips
/>
Also there is clearable prop that adds clear button on the right that clear all chips via one click (works for both Vuetify versions):
<v-combobox
...
clearable
/>

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>

Hide tab headers in vuetify

I'm using #nuxtjs/vuetify with nuxt.js as combo.
I'm using the component v-tabs with v-tabs-items in my page used as grouped settings.
What i want to achieve is simple but i couldn't find the solution.
if there is just one group of settings. i don't want to show the tab header.
if there are multiple groups of settings. then the tab headers may show.
so what the code is now:
<v-tabs v-model="tab"> <!-- GROUPED SETTINGS ORDERED IN TABS -->
<v-tab class="mx-4" v-for="(tab, index) in Object.keys(content_fields)" :key="index">{{ tab }}</v-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item>
<!-- item -->
</v-tab-item>
</v-tabs-items>
i tried to do a v-if on the v-tabs. so if there is just one group the v-tabs will not show.
but then the v-tabs-items will not show at all. even if i set the tab property on the good tab.
is there any solution that will give the result that i want?
I did with v-show:
HTML:
<div id="app">
<v-app id="inspire">
<v-tabs>
<v-tab v-show="tabs.length > 1" v-for="i in tabs" :key="i.name" :href="`#tab-${i.name}`"> {{ i.text }} </v-tab>
<v-tab-item v-for="i in tabs" :key="i.name" :value="'tab-' + i.name">
<v-card flat tile>
<v-card-text>{{ i.text }}</v-card-text>
</v-card>
</v-tab-item>
</v-tabs>
</v-app>
</div>
JS:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
tabs: [
{ name: 1, text: 'One' },
//{ name: 2, text: 'Two' }
]
}
}
})
Here you can see the live demo: https://codepen.io/ljcordero/pen/wvBXqWv
Hope this help.

Vuetify card with pagination

I have a code pen https://codepen.io/handy29/pen/YzPNWpO inside this codepen I have vuetify 2.x.x. I want implement v-card using pagination, as you can see I have a headline array and subtitle array. If you click right arrow or left arrow, headline and subtitle inside v-card change (adjust index of array) but as you can see for now this is not working. I tried using v-model="page" on v-card and using v-slot:items on v-list-item-title, but still this is not working.
HTML:
<div id="app">
<v-app id="inspire">
<v-card
:items="headlines"
v-model="page"
class="mx-auto"
max-width="344"
outlined
>
<v-list-item three-line>
<v-list-item-content>
<div class="overline mb-4">OVERLINE</div>
<v-list-item-title class="headline mb-1" v-slot:items="props">{{props}}</v-list-item-title>
<v-list-item-subtitle>This is subtitle (will be replace using array subtitle)</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-avatar
tile
size="80"
color="grey"
></v-list-item-avatar>
</v-list-item>
<v-card-actions>
<v-btn text>Button</v-btn>
<v-btn text>Button</v-btn>
</v-card-actions>
</v-card>
<v-pagination
v-model="page"
:length="headlines.length"
></v-pagination>
</v-app>
</div>
JS:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
page: 1,
headlines: ['headline1', 'headline2', 'headline3', 'headline4', 'headline5'],
subtitles: ['Greyhound divisely hello coldly fonwderfully', 'Greyhound divisely hello sunny wonderful', 'Flower expected coldly fonwderfully', 'Flower sunny hello', 'Greyhound flower']
}
},
})
What should I do?
Don't understand why you need the v-slot:items="props".
But your problem could be solved just by changing the following bindings,
<v-list-item-title class="headline mb-1"> {{ headlines[page - 1] }}</v-list-item-title>
<v-list-item-subtitle>{{ subtitles[page - 1] }}</v-list-item-subtitle>

Weird Vue Render issue on embedded list that uses Vuetify Badges

I have a list of users that I'm trying to add an icon to enable/disable their account. So I render the list of users in a v-for loop. And I track the visibility of a badge on each icon with an array indexed by the loop index.
I'm having a weird issue where Vue doesn't recognize the change to the array value unless I update some other dummy variable. Is this a Vuetify issue. Or just a Vue Reactivity issue?
I created a Codepen that show my issue.
https://codepen.io/DedicatedManager/pen/eYmZRQo?editors=1010
<div id="app">
<v-app id="inspire">
<v-container fluid class="text-center">
The two lists are the same differing only by changing a dummy variable in the "mouseover" and "mouseout" function that causes the badge to display/hide. But the one on the left doesn't show the badge on hover. The one on the right has the extra dummy variable that somehow forces the rendering to work and thus the badge shows (it shows on both sides because they use the same variable to hold the boolean for the v-model.
<v-row
justify="space-between"
>
<v-col cols="6" class="mt-12">
<div v-for="(listItem,index) in myData" :key="index">
<v-badge v-model="showCircle1[index]" overlap>
<template v-slot:badge>
<span><v-icon>mdi-delete</v-icon></span>
</template>
<v-icon large color="grey" #mouseover="showCircle1[index]=true;" #mouseout="showCircle1[index]=false;">mdi-email</v-icon>
</v-badge>
</div>
</v-col>
<v-col cols="6" class="mt-12">
<div v-for="(listItem,index) in myData" :key="index">
<v-badge v-model="showCircle1[index]" overlap>
<template v-slot:badge>
<span><v-icon>mdi-delete</v-icon></span>
</template>
<v-icon large color="grey" #mouseover="showCircle1[index]=true; mouseOverVal=true" #mouseout="showCircle1[index]=false; mouseOverVal=false">mdi-email</v-icon>
</v-badge>
</div>
</v-col>
</v-row>
mouseOverVal: {{mouseOverVal}}<br>
showCircle1: {{showCircle1}}<br>
</v-container>
</v-app>
</div>
Javascript
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
showCircle1:[false,false,false,false],
myData:['one','two','three','four'],
mouseOverVal:false,
}
},
})
Vue cannot detect changes when you set the value of an item in an array.
From https://v2.vuejs.org/v2/guide/list.html#Caveats:
For example:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // is NOT reactive
vm.items.length = 2 // is NOT reactive
[...] both of the following will accomplish the same
as vm.items[indexOfItem] = newValue, but will also trigger state
updates in the reactivity system:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
See the link for further details and additional related approaches.