Article image

HOW TO MAKE A SMALL NOTIFICATION PLUGIN IN VUE

Javascript -

Oct 02 2019

Dino Numic

Notifications are a great way to let the user know about the status of his actions on the website. For example, on a cms we would have options to create, update, delete various records. It would be cool to pop some notification on the screen after one of these actions executes. Our goal is to create a reusable piece of code that we can bring to any Vue project.

I will create a simple index component with buttons to test our notification plugin. I will reuse Laravel welcome page.

<div id="app">

    <div class="container-fluid">
        <div class="row"><index-component></index-component></div></div>

</div>

I also have to include js and css to my head.

<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">

<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>

I will rename ExampleComponent to IndexComponent and add some buttons with empty methods for now.

<template>
    <div class="container"><div class="row justify-content-center"><div class="col-md-8"><div class="card"><div class="card-header">Index Component</div>

                    <div class="card-body">
                        <button @click="successNotification" class="btn btn-sm btn-success">Success</button><button @click="warningNotification" class="btn btn-sm btn-warning">Warning</button><button @click="infoNotification" class="btn btn-sm btn-info">Info</button><button @click="dangerNotification" class="btn btn-sm btn-danger">Danger</button></div>

                </div>
            </div>
        </div>
</div>
</template>
<script>
export default {
        methods: {
            successNotification() { },
            warningNotification() { },
            infoNotification() { },
            dangerNotification() { }
        }
    }
</script>

Now we can start working on our notification plugin. Plugins in Vue are a great option for adding some global functionality to Vue applications. For example, a notification plugin. Vue Router is also a good example of a Vue plugin.

Aside from creating a good name, we also need to expose the install method from our plugin. Install method will be called with the Vue constructor as the first argument and additional options that can customize the behavior of the plugin as the second argument.

This is the starting point of a Vue plugin.

export default {
  install (Vue, options) {
    //
  }
}

I will call it notifier. Seems appropriate enough. Let's create a plugins folder inside resources/js and create a notifier folder. Inside notifier we will create two files and a css folder for styles.

In index.js we will have all our logic of how to create a new notification. Here, we will expose install method for Vue constructor. Our plugin will work like this. We call this.$notifier with one of the four properties available. From there, we will create a constructor for Notifier component with Vue.extend(), merge all passed options, and finally create a new instance of Notifier.

import Notifier from './Notifier';
import './css/styles.css';

const NotifierVue = {

    // Expose install method
    install(Vue, options) {

         // Construct a new Notification component

         function createNotifier(obj, type) {const NotificationComponent = Vue.extend(Notifier);
            let title, message;

            // Set default title and message

            switch(type){case 'success':
                    title = 'Success Message';
                    message = 'Action was successful';
                    break;
                case 'error':
                    title = 'Error Message';
                    message = 'Action was unsuccessful';
                    break;
                case 'info':
                    title = 'Info Message';
                    message = 'Action was successful';
                    break;
                case 'warning':
                    title = 'Warning Message';
                    message = 'Action requires your attention';
                    break;
            }

            let defaultTitleMessage = {
                title: title,
                message: message
            };

            // Merge all present options

            let props = Object.assign(defaultTitleMessage, options, obj, { type : type });return new NotificationComponent({
                el: document.createElement('div'),
                propsData: props
            });
        }

        // Add Vue instance methods which will create appropriate notifier
        Vue.prototype.$notifier = {
            success(obj) {
                return createNotifier(obj, 'success');
            },
            error(obj) {
                return createNotifier(obj, 'error');
            },
            warning(obj) {
                return createNotifier(obj, 'warning');
            },
            info(obj) {
                return createNotifier(obj, 'info');
            }
        }
    }
};

export default NotifierVue;

In Notifier.vue component we will have our markup code for the notification. We have four props. Title, message or body, type, and duration of notification. Our notifier will be able to display a single or an array of messages, such as, for example, you get with validation errors. In beforeMount() method it will check if notification container already exists, otherwise, it will create a new one. After it is mounted, it will wait for the duration to expires upon which it will destroy itself.

<template>
    <div v-if="show" :class="'notify-'+type" class="notifier notifier-shadow"><p>{{ title }}</p><p class="object-message"v-if="typeof message === 'array'" v-for="text in message"
        >
            {{ text }}
        </p><p v-else>{{ message }}</p></div>
</template>
<script>export default {
        props: {
            title: {
                type: String
            },
            message: {
                type: [String, Array]
            },
            type: {
                type: String
            },
            duration: {
                type: Number,
                default: 2000
            },
        },
        data() {
            return {
                show: true
            }
        },
        beforeMount() {
            // Look for notification containerlet container = document.getElementById('notification-container');

            // If not found create new container and append to body
            if (!container) {
                container = document.createElement('div');
                container.id = 'notification-container';
                document.body.append(container);
            }

            // Append notification to container
            container.appendChild(this.$el);
        },
        mounted() {
            // Destroy notification after specified duration timelet self = this;
            setTimeout(()=>{
                self.show = !self.show;
                self.destroyNotifier();
            }, this.duration);
        },
        methods: {
            destroyNotifier(){
                setTimeout(()=>{
                    this.$destroy();
                }, 500);
            }
        }
    }
</script>

In style.css we have notifier styles. Nothing here that requires explaining.

#notification-container{
    z-index: 9999;
    padding: 10px;
    position: fixed;
    top: 0;
    right: 0;
}

.notifier{
    margin-bottom: 10px;
    width: auto;
    height: auto;
    padding: 7px 100px 0 10px;
    border-radius: 5px;
}

.notifier > p:nth-child(1){
    color: #fff;
    font-size: 1.15rem;
    margin-bottom: 2.5px;
}

.notifier > p:nth-child(2){
    color: #fff;
    margin: 0;
    padding: 0 0 5px 0;
}

.notifier-shadow{
    box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4);
}

.notifier:hover{
    cursor: pointer
}

.notify-success{
    background-color: rgba(40, 167, 69, 0.95) !important;
}
.notify-error{
    background-color: rgba(220, 53, 69, 1) !important;
}
.notify-warning{
    background-color: rgba(255, 193, 7, 1) !important;
}
.notify-info{
    background-color: rgba(23, 162, 184, 1) !important;
}

.object-message {
    color: #fff;
    margin: 0;
    padding: 0 0 5px 0;
}

Let's update our Index Component methods. This is an example how you would call our notifier.

export default {
    methods: {
        successNotification() {
            this.$notifier.success({
                title: 'Success',
                message: 'Success button clicked',
                duration: 2000
            });
        },
        warningNotification() {
            this.$notifier.warning({
                title: 'Warning',
                message: 'Warning button clicked',
                duration: 2000
            });
        },
        infoNotification() {
            this.$notifier.info({
                title: 'Info',
                message: 'Info button clicked',
                duration: 2000
            });
        },
        dangerNotification() {
            this.$notifier.error({
                title: 'Danger',
                message: 'Danger button clicked',
                duration: 2000
            });
        }
    }
}

The only thing left to do is to register our plugin

// Vue Notifier
import VueNotifier from './plugins/notifier/index';
Vue.use(VueNotifier);

This is the final look of our notifier.

You can easily modify this plugin to your hearts desire by adding additional props and passable options. From position, styles, message handlers, etc.