Currently it is not easy to handle interaction of multiple snackbar messages. They can overlap and hide each other. Moreover, because they are added as components inside other components, if parent component is removed (for example, because of a route change), snackbar message also disappears or is even not shown.
I would propose that there is a global queue of snackbar messages. Maybe by sending a Vue event. Some options could then be to display messages one after the other, or display them stacked, up to some number.
Currently it seems one has to manually implement whole logic how to handle interaction between multiple snackbar messages.
EDIT: I have made a NPM package with this functionality @tozd/vue-snackbar-queue.
The reason for not currently supporting multiple snackbar messages is that MD spec specifies that only one snackbar should be displayed at a time.
However, several people have made similar requests previously. And I think there is some merit to at least include basic support for displaying consecutive snackbar messages. Not a fan of providing a global queue in vuetify though.
Will leave this open for discussion.
One could create a component (say SnackDisplayer) and, using Vuex and stores, displaying the snackbar outside of any component (well... except the 1st level component containing the v-app tag).
I tried this with a quite successful result. Here is a fiddle that demonstrate this:
http://jsfiddle.net/13uc6mu5/
With this configuration (check fiddle source), one can call this.$store.commit('msg/set', { message: 'hello' })
in any component and the snack pop up.
One thing that can be improved is indeed the possibility to have consecutive snackbars.
I think it'd make sense to allow the v-snackbar
to handle the message queue internally.
The reason for not currently supporting multiple snackbar messages is that MD spec specifies that only one snackbar should be displayed at a time.
So one way could be that they are queued and displayed one after another. But optional stacking would be cool as well. (Or maybe one message, but with a badge in the corner showing that there are many other pending behind this one.)
Is there some other more MD-approved approach to display multiple messages to the user?
The way Google applies the MD specs is closing the opened snackbar/toast when a new one is dispatched. You can see it in the new Google Calendar web version (more info), Angular Material and Angular Material2.
I wrote a little mixin to provide a snackbar queue, it can be easily incorporated in your app: https://codepen.io/jayblanchard/pen/yoxPRY
@Phlow2001, oh so easy. Thanks!
I would love to also have a badge on the side of the snackbar, to show how many pending snackbars are in the queue, but it is not as easy as I expected: #2392
@Phlow2001 But your mixin does not align to the MD specs that @peluprvi mentioned.
The way Google applies the MD specs is closing the opened snackbar/toast when a new one is dispatched.
@pSchaub Based on the code of @Phlow2001, this code should aligns with the MD specs:
addNotification(text) {
this.notification = false
setTimeout(() => {
this.notificationText = text
this.notification = true
}, 250)
}
No snackbar queue, a new notification will replace the current one.
Codepen: https://codepen.io/manuel-di-iorio/pen/YeyVMb
MD example: https://material.angular.io/components/snack-bar/examples
I've been experimenting around with a queue system for the dialogs. I also want to spawn them programatically so here is what I have so far. Let it be an inspiration to whatever feature you guys are building, I'm not confident enough in it to PR it.
index.js
import Vue from 'vue'
import Toast from './Toast'
let queue = []
let showing = false
export { Toast }
export default {
open(params) {
if (!params.text) return console.error('[toast] no text supplied')
if (!params.type) params.type = 'info'
let propsData = {
title: params.title,
text: params.text,
type: params.type
}
let defaultOptions = {
color: params.type || 'info',
closeable: true,
autoHeight: true,
timeout: 1000,
multiLine: !!params.title || params.text.length > 80
}
params.options = Object.assign(defaultOptions, params.options)
propsData.options = params.options
// push into queue
queue.push(propsData)
processQueue()
}
}
function processQueue() {
if (queue.length < 1) return
if (showing) return
console.log(queue)
let nextInLine = queue[0]
spawn(nextInLine)
showing = true
queue.shift()
}
function spawn(propsData) {
const ToastComponent = Vue.extend(Toast)
return new ToastComponent({
el: document.createElement('div'),
propsData,
onClose: function() {
showing = false
processQueue()
}
})
}
toast.vue
<template>
<v-snackbar v-model="open" v-bind="options">
<div class="ctn">
<div class="title mb-2" v-if="title">{{title}}</div>
<div class="txt">{{text}}</div>
</div>
<v-btn v-if="options.closeable" flat icon @click.native="open = false">
<fai :icon="['fal', 'times']" size="lg"></fai>
</v-btn>
</v-snackbar>
</template>
<script>
/* TODO */
export default {
name: 'toast',
props: {
title: String,
text: String,
type: String,
options: Object,
},
data() {
return {
open: false,
}
},
watch: {
open: function(val) {
if (!val) {
this.close()
}
},
},
beforeMount() {
document.querySelector('#app').appendChild(this.$el)
},
mounted() {
this.open = true
},
methods: {
close() {
if (this.open) this.open = false
setTimeout(() => {
this.$options.onClose()
this.$destroy()
removeElement(this.$el)
}, 700) // wait for close animation
},
},
}
function removeElement(el) {
if (typeof el.remove !== 'undefined') {
el.remove()
} else {
el.parentNode.removeChild(el)
}
}
</script>
main.js
...
import Toast from './components/toast'
Vue.prototype.$toast = Toast
...
Then just use this.$toast.open({text: 'test'})
and it should show a dialog.
Use at your own risk.
Another Mixin that somebody gave me on the discord channel, sorry I don't remember who
export default {
data: () => ({
toast: {
text: 'I am a Snackbar !',
color: 'success',
timeout: 5000,
top: true,
bottom: false,
right: false,
left: false,
multiline: false
},
notificationQueue: [],
notification: false
}),
computed: {
hasNotificationsPending () {
return this.notificationQueue.length > 0
}
},
watch: {
notification () {
if (!this.notification && this.hasNotificationsPending) {
this.toast = this.notificationQueue.shift()
this.$nextTick(() => { this.notification = true })
}
}
},
methods: {
addNotification (toast) {
if (typeof toast !== 'object') return
this.notificationQueue.push(toast)
if (!this.notification) {
this.toast = this.notificationQueue.shift()
this.notification = true
}
},
makeToast (text, color = 'info', timeout = 6000, top = true, bottom = false, right = false, left = false, multiline = false, vertical = false) {
return {
text,
color,
timeout,
top,
bottom,
right,
left,
multiline,
vertical
}
}
}
}
Usage:
this.addNotification({text: 'Some Text', color: 'danger', timeout: 5000})
this.addNotification({text: 'Some Text', color: 'danger', timeout: 5000, top: false, bottom: true, multiline: true})
I have used buefy
before and it has a neat implementation for this. There is no need for the component. The $snackbar
is available on the Vue instance. This saves me a lot of hassle about putting the data into state first and then using it to display the snack bar. They do something like this:
this.$snackbar.open(`Default, positioned bottom-right with a green 'OK' button`)
This can be triggered by any network call, vuex action, etc.
@ankitsinghaniyaz my example is inspired by buefy, you can see the same index structure
Agree to @ankitsinghaniyaz buefy has a nice implementation for this
A couple of months ago I added a component to NPM that handles this and more.
https://www.npmjs.com/package/snackbarstack
Here's a demo
https://codepen.io/Flamenco/full/ZoRvLw/
@Flamenco no github repo for it?
@aldarund It's using a few of our internal libraries I can't open source at this moment; The binaries are free to distribute however.
@Flamenco thanks, pretty nice. But hard to customize it. Such as change position, color etc.
@raphaelsoul Some of those are on our wish list in the todo section of NPM. If I can get to it, I will post a github project to collect feature requests.
What kind of API would you suggest for _change position_?
@Flamenco cool demo, thanks for nice job!
Is it not on GihHub? I'd love to fork it and add a stacking feature
@shershen08 This is not open source yet. We have a collection of Vuetify utilities that will most likely be released under a CC license.
I did a more or less simple CSS Hack i want to share if anybody feels like going in the same direction.
WARNING this will brake vuetify default styling and possibility to change positioning the default way
HTLM
<div id="snackbar">
<v-snackbar
v-for="(snackbar, index) in snackbars"
v-model="snackbar.show"
:multi-line="true"
:right="true"
:timeout=6000
:top="true"
:color="snackbar.color"
>
{{ snackbar.text }}
<v-btn
flat
@click="hideSnackbar(index)"
>
<v-icon>close</v-icon>
</v-btn>
</v-snackbar>
</div>
SCSS
#snackbar{
position: fixed;
top: 0;
right: 0;
z-index: 9999999999;
display: flex;
width: 100vw;
flex-direction: column-reverse;
align-items: flex-end;
justify-content: space-around;
div{
position: relative !important;
margin-bottom: 8px;
}
}
//vuex-store
snackbars : [],
//vuex-mutations
showSnackbar ( state, value) {
state.snackbars.push( value )
},
hideSnackbar ( state, index ) {
state.snackbars[index].show = false;
},
Global Access
Vue.mixin( {
methods : {
showSnackbar ( color, text ) {
this.$store.commit( 'showSnackbar', {
'show' : true,
'color' : color,
'text' : text
} )
},
hideSnackbar ( index ) {
this.$store.commit( 'hideSnackbar', index )
}
}
} )
//Usage in component
this.showSnackbar('info','bla')
any news here if this will become a standard vuetify feature?
I made in meantime: https://www.npmjs.com/package/@tozd/vue-snackbar-queue
I've just added this functionality into my toast component if anyone interested: https://www.npmjs.com/package/vuetify-toast-snackbar
Why does the snackbar not support newlines (neither \n
nor <br>
)?
I wrote a little mixin to provide a snackbar queue, it can be easily incorporated in your app: https://codepen.io/jayblanchard/pen/yoxPRY
Upgrade to version 2.0.4 and it no longer works.
When you have 1/2 a dozen user libraries for the functionality, it's probably time to re-evaluate if this is a feature that should be included in Vuetify.
@Flamenco A repo would be great! And the API for it should at least match that of the Vuetify one with w/e else you add to it.
We removed or open sourced most of the dependent proprietary libraries from our project at https://www.npmjs.com/package/snackbarstack. We will change the license and contribute the source to Vuetify there is interest.
I wrote a little mixin to provide a snackbar queue, it can be easily incorporated in your app: https://codepen.io/jayblanchard/pen/yoxPRY
Upgrade to version 2.0.4 and it no longer works.
Just add vuetify: new Vuetify(),
to Vue component
A couple of months ago I added a component to NPM that handles this and more.
https://www.npmjs.com/package/snackbarstackHere's a demo
https://codepen.io/Flamenco/full/ZoRvLw/
This installs another vuetify
version 馃槄 . vuetify
dependency should be a dev/peer dependency instead in your package.json, @Flamenco ?
@nelson6e65 You are correct. It should not be bundled. Vue is also not marked as peer dependency.
I think this should actually be marked as an external in webpack. I added a a project to my github repo and added this as an issue there. Should be fixed in few hours.
@nelson6e65 The package.json was updated, and I added initial support for Vuetify 2.x
@nelson6e65 The package.json was updated, and I added initial support for Vuetify 2.x
Nice! I'll give a try.
Thanks!
I put together this using Vuex to store messages and queue notifications app-wide.
Please feel free to check it out: https://codesandbox.io/s/queable-snackbars-notification-d5zi6
It may still needs some improvements, but I didn't manage to make a repo yet
Yet another one: https://github.com/Aymkdn/v-snackbars
This one is different from the others because it will show all the snackbars in a stack way:
For all who want stacking snackbars, the material design specification discourages stacking snackbars. See https://material.io/components/snackbars#behavior
I'm sure it can be added to Vuetify, but it would be departure from following the spec.
Yes, this is why my original issue suggests both showing them one after the other, in a queue, or stacking them. My own plugin does the former to be according to the spec: https://www.npmjs.com/package/@tozd/vue-snackbar-queue
So yea, this issue is not just about stacking, but in general about support for having multiple messages in flight. How is then this shown should probably be configurable.
Try this tutorial which gives you the ability to create stacked notifications with the tiniest overhead possible
Most helpful comment
Yet another one: https://github.com/Aymkdn/v-snackbars
This one is different from the others because it will show all the snackbars in a stack way:
