I have a scenario like I have to list a few hundred categories and I have to show them in a select box. Since the list is huge, skip and limit is implemented in the backend, so that it will limit the categories to 20s. My case is like when the user sees the first 20 categories, in the end, I have to add some button stating like 'Load more' so that when the user clicks on it, they can see the next 20 categories. But I have no idea how to add a button in a vuetify select. Can someone help me with this?
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
fruits: [
'Item 1',
'Item 2',
'Item 3',
'Item 4'
],
selectedFruits: [],
}),
computed: {
likesAllFruit () {
return this.selectedFruits.length === this.fruits.length
},
likesSomeFruit () {
return this.selectedFruits.length > 0 && !this.likesAllFruit
},
icon () {
if (this.likesAllFruit) return 'mdi-close-box'
if (this.likesSomeFruit) return 'mdi-minus-box'
return 'mdi-checkbox-blank-outline'
},
},
methods: {
toggle () {
this.$nextTick(() => {
if (this.likesAllFruit) {
this.selectedFruits = []
} else {
this.selectedFruits = this.fruits.slice()
}
})
},
loadMore () {
console.log('load more ...')
}
},
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Material+Icons" 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.2.26/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.2.26/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-select
v-model="selectedFruits"
:items="fruits"
label="Favorite Fruits"
multiple
>
<template v-slot:prepend-item>
<v-list-item
ripple
#click="toggle"
>
<v-list-item-action>
<v-icon :color="selectedFruits.length > 0 ? 'indigo darken-4' : ''">{{ icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>Select All</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-divider class="mt-2"></v-divider>
</template>
<template v-slot:append-item>
<v-divider class="mb-2"></v-divider>
<v-btn color="primary" #click="loadMore">
Click me for load more items.
</v-btn>
</template>
</v-select>
</v-container>
</v-app>
</div>
In Vuetify 2.x there is an virtual scroll component, but I still found the v-intersect easiest to use...
markup:
<v-autocomplete
v-model="selected"
:items="beers"
item-text="name"
item-value="id"
label="Search da beers.."
return-object
autocomplete="off"
>
<template v-slot:append-item>
<div v-intersect="onIntersect" class="pa-4 teal--text">
Loading more items ...
</div>
</template>
</v-autocomplete>
data() {
return {
beers: [],
selected: null,
perPage: 30,
page: 1,
}
},
methods: {
getBeers() {
console.log('page', this.page)
const apiUrl = `https://api.punkapi.com/v2/beers?page=${this.page}&per_page=${this.perPage}`
axios.get(apiUrl)
.then(response => {
this.beers = [
...this.beers,
...response.data
]
})
},
onIntersect () {
console.log('load more...')
this.page += 1
this.getDate()
},
},
created() {
this.getData()
}
Working Codeply
Related
I have these combobox chips with a prob deletable-chips
<v-combobox
v-model="selectedCategories"
:items="attributeCategories"
item-text="name"
item-value="id"
label="Category"
multiple
chips
clear-icon="mdi-close-circle"
deletable-chips
v-on:change="changeCategory(selectedCategories)"
></v-combobox>
Is there a way to prevent a specific chip to be deleted? For example not show the remove button on a specific one? Let's say for Device and only allow Weather and Geo Location to me removed
Instead of using in-built delete method of v-chips. You can do the implementation via custom #click:close event. I created a working demo for you :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
model: [],
items: [
{
text: 'Weather'
},
{
text: 'Geo Location'
},
{
text: 'Device'
}
]
}),
methods: {
remove (itemText) {
if (itemText === 'Device') {
return;
} else {
this.model.forEach(obj => {
if (obj.text === itemText) {
this.model.splice(this.model.indexOf(obj), 1)
}
})
this.model = [...this.model]
}
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://unpkg.com/#mdi/font#6.x/css/materialdesignicons.min.css"/>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-combobox
v-model="model"
:items="items"
label="Select"
multiple
small-chips
>
<template v-slot:selection="{ attrs, item, parent, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close="false"
#click:close="remove(item.text)"
>
<span class="pr-2">
{{ item.text }}
</span>
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
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.
new Vue({
el: '#app',
vuetify: new Vuetify({
theme: {
dark: true
},
}),
data() {
return {
loading: null,
requests: [{"req_id":78},{"req_id":79}],
tableHeaders: [
{
text: 'ID',
value: 'req_id',
align: 'center',
},
{ text: 'Actions', value: 'action', sortable: false, align: 'center'},
],
createloading: false,
cancelloading: false,
};
},
methods: {
createPacks(item) {
this.loading = !this.loading;
item.createloading = true;
},
excludeRequest(item) {
this.loading =!this.loading;
item.createloading = false;
},
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<div id="app">
<v-app>
<v-row no-gutters class="align-start justify-start">
<v-col>
<v-row>
<v-data-table
:headers="tableHeaders"
:items="requests"
:loading="loading"
>
<template v-slot:item.action="{ item }">
<v-btn color="success" #click="createPacks(item)" :loading="item.createloading" :disabled="item.createloading">Create</v-btn>
<v-btn color="error" #click="excludeRequest(item)" :loading="item.cancelloading" :disabled="!item.createloading">Cancel</v-btn>
</template>
</v-data-table>
</v-row>
</v-col>
</v-row>
</v-app>
</div>
Please help me implement this code correctly, as it does not function correctly. The buttons should correctly show the loading indicator and should be independent.
Thank you in advance for your generosity.
Below is a link to codepen.
https://codepen.io/protemir/pen/MWwGaeO
{
path: '/link_1',
name: 'link_1',
component: () => import('./views/Link1.vue')
},
It is possible to have it one path like /link_1 but every time when go to this route load different component.
Like: First time when go to /link_1 load Link1.vue and second time when user go to /link_1 load and display Link2.vue.
You can use a combination of watch and <component> to render a dynamic component each time the link is clicked.
For example, this generates 100 components named component1 through component100, rendering one at random each time the <router-link></router-link> is clicked:
Vue.use(VueRouter)
const router = new VueRouter({
routes: [{
path: '/random/:id'
}]
})
const components = Array.from(Array(100), (x, i) => {
return {
name: `component${ i+ 1 }`,
props: ['lorem'],
template: `
<v-card>
<v-card-title>
<v-avatar>
<span class="blue-grey--text headline">${i + 1}</span>
</v-avatar>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-container fluid>
<v-layout justify-center>
<v-flex>
<span class="subheader" v-html="lorem"></span>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
</v-card>
`
}
}).reduce((carry, c) => {
carry[c.name] = c
return carry
}, {})
new Vue({
el: '#app',
components,
router,
computed: {
current() {
return `component${this.cid}`
}
},
data() {
return {
cid: 1,
lorem: 'What mystery does the next page hold?'
}
},
watch: {
'$route': {
handler: function() {
let id = this.cid
while (this.cid === id) {
id = Math.floor(Math.random() * 100) + 1
}
this.cid = id
fetch('https://baconipsum.com/api/?type=all-meat¶s=3&format=html').then(res => res.text()).then(data => {
this.lorem = data
})
}
}
}
})
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
<script src="https://unpkg.com/vue-router#3.0.2/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-container>
<v-toolbar app>
<v-toolbar-items>
<v-btn :to="`/random/${cid}`" color="deep-orange darken-4" dark>Click Me</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-content>
<v-slide-x-transition leave-absolute mode="out-in">
<component :is="current" :lorem="lorem"></component>
</v-slide-x-transition>
</v-content>
</v-container>
</v-app>
</div>
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;
}
}
},