Global Notifications with Vue

by Thorsten

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.1.2/foundation.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.1/animate.min.css">
<div id="app">
  <notifications></notifications>
  <div>
    <button class="button success" @click="showSuccessMessage">Show Success Message</button>
  </div>
  <div>
    <button class="button alert" @click="showAlertMessage">Show Alert Message</button>
  </div>
   <div>
    <button class="button primary" @click="showStickyMessage">Show Sticky Message</button>
  </div>
  <p>(You can click each button multiple times to add multiple Notifications)</p>
</div>

<template id="notifications">
  <div class="notifications">
    <notification v-for="notification in notifications" :notification="notification" @close-notification="removeNotification" transition="fade">
    </notification>
  </div>
</template>

<template id="notification">
  <div class="notification callout animated" :class="notification.type ? notification.type : 'secondary'" transition="fade">
    <button @click="triggerClose(notification)" class="close-button" aria-label="Close alert" type="button">
      <span aria-hidden="true">&times;</span>
    </button>
    <h5 v-if="notification.title">{{notification.title}}</h5>
    <p>
      {{notification.text}}
    </p>
  </div>
</template>

SCSS

.notifications {
    position: fixed;
    right: 10px;
    top: 10px;
    width: 350px;
    z-index: 1;
  }
  .notification p {
    margin-right: 20px;
  }

Babel + JSX

const NotificationStore = {
  state: [], // here the notifications will be added

  addNotification: function (notification) {
    this.state.push(notification)
  },
  removeNotification: function (notification) {
    this.state.$remove(notification)
  }
}

var Notification = Vue.extend({
	template: '#notification',
  props: ['notification'],
  data: function () {
  	return { timer: null }
	},
  ready: function () {
  let timeout = this.notification.hasOwnProperty('timeout') ? this.notification.timeout : true
  if (timeout) {
  	let delay = this.notification.delay || 3000
      this.timer = setTimeout(function () {
        this.triggerClose(this.notification)
      }.bind(this), delay)
    }
  },

  methods: {
    triggerClose: function (notification) {
    	clearTimeout(this.timer)
      this.$dispatch('close-notification', notification)
    }
  }
})

var Notifications = Vue.extend({
	template: '#notifications',
  components: {
    notification: Notification
  },
  data () {
    return {
      notifications: NotificationStore.state
    }
  },
  methods: {
    removeNotification: function (notification) {
      NotificationStore.removeNotification(notification)
    }
  }
})

Vue.transition('fade', {
  enterClass: 'fadeInDown', // class of animate.css
  leaveClass: 'fadeOutDown' // class of animate.css
})

var App = new Vue({
  el: '#app',
  components: {
  	'notifications': Notifications
  },
  data() {
    return {
			// no Data Nessessary. Notifications are stored in the NotificationStore
    }
  },
  methods: {
  	showSuccessMessage: function () {
    	NotificationStore.addNotification({
      	title: "Success!!",
        text: "A Success Message",
        type: "success",
        timeout: true
      })
    },
    showAlertMessage: function () {
    	NotificationStore.addNotification({
        text: "An Alert Message, without a title",
        type: "alert"
        // timeout not specified - defaults to true
        // delay not specified, defaults to 3000
   ...