DEV Community

Mark Anderson
Mark Anderson

Posted on

Vue Socket.io and Toast Messages

I've decided against this solution but am posting it so that others can see/use the example if it fits their needs and so I can explain why I went another route.

This solution breaks "Keep It Simple Stupid" (KISS) for me. i.e. Load balancing is too complex for the trade-off of having direct server communication, Sticky load balancing. For many of the smaller apps, a single server instance is more than adequate. So am I violating "You aren't gonna need it" (YAGNI)? Possibly but since neither WebSockets nor socket.io are currently being used I think it's better to invest research time in an honest comparison/evaluation upfront. I'll post next on a WebSocket example that may prove to be a better solution.

Rather than parse response messages and display them individually upon each request we will have the messages sent back via socket.io to our app and displayed using global message queues for success, info and oops. This is a "minimal" installation for testing/proof of concept. In our case, we will be simulating logging into a server and returning appropriate toasts based on the credentials.

server

We will first create a server.js file to listen to the request. If using node and npm as commands show below I suggest putting it in an src directory.

server.js

var http = require('http');
var app = http.createServer();

// Socket.io server listens to our app
var io = require('socket.io').listen(app);

io.on('connection', function (socket) {
  // You will get this message when the server becomes 
  // available or a new socket it created
  socket.emit('success', {message: 'Server Accecpting Connections'});
  // This will send a message letting users know the server is
  // being sutdown.
  process.on('SIGINT', () => {
    io.emit('oops', {message: 'Server Shut Down'});
    process.exit();
  });
  // This handles the authentication and related messages.
  socket.on('authenticate', function (payload) {
    let data = JSON.parse(payload.data);
    if (data.password == 'passwd' && data.username == 'admin') {
      // This is managed in the Vue.js since it is not a feedback message.
      socket.emit('auth', { jwt: 'Generated JWT Token'} );
      // We emit to two seperate message queues that are handled in store.js
      // so they are universal.
      socket.emit('success', { message: 'You are logged in' });
      socket.emit('info', { message: 'JWT Token Attached', jwt: 'GeneRAtEdJwTOken' });
    } else {
      // error message got picked up so changed to opps handled in store.js
      socket.emit('oops', { message: 'Invalid Credentials Supplied' })
    }
  });
});

app.listen(8910);
console.log('Server listening on 8910');

Using the following command to get started then copy the server.js into the src directory. Once that is done you can start the server using node server.js or node src/server.js.

mkdir server
cd server
npm init
npm install socket.io
mkdir src

Vue Client

Once you have a new minimal vue project you should replace/create the three files listed below. Assuming you were able to create the vue app and replaced/created the files below you should be able to get the client running with npm run serve.

Initialization of vue project

Below are the commands I used to get a test vue instance up and running. The vue create command has question response so run it first then you can copy-paste the rest.

vue create --no-git --bare --skipGetStarted socketio_test_front
cd socketio_test_front
npm install --save vue-socket.io
npm install --save vuex
npm install --save vuex-persist
npm install --save es6-promise
npm install --save vue-toasted

main.js

import Vue from 'vue'
import store from './store'
import App from './App.vue'
import VueSocketIO from 'vue-socket.io'
import Toasted from 'vue-toasted';

Vue.use(Toasted);

// Lets Register a Global Toasts.
Vue.toasted.register('appError',
  (payload) => {
    if (!payload.message) {
      return "Message not definded."
    }
    return payload.message;
  },
  {
    type: 'error'
  });

Vue.toasted.register('appSuccess',
  (payload) => {
    if (!payload.message) {
      return "Message not definded."
    }
    return payload.message;
  }, {
  type: 'success'
});

Vue.toasted.register('appInfo',
  (payload) => {
    if (!payload.message) {
      return "Message not definded."
    }
    return payload.message;
  }, {
  type: 'info'
});

// Now setup our socket and vuex configuration
Vue.use(new VueSocketIO({
  debug: true,
  connection: 'http://localhost:8910',
  vuex: {
    store,
    actionPrefix: 'SOCKET_'
  },
  options: {} //Optional options
}))

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
    },
    actions: {
        "SOCKET_oops"(state, server) {
            Vue.toasted.global.appError({
                message: server.message
            }).goAway(1200);
        },
        "SOCKET_success"(state, server) {
            Vue.toasted.global.appSuccess({
                message: server.message
            }).goAway(1200);
        },
        "SOCKET_info"(state, server) {
            Vue.toasted.global.appInfo({
                message: server.message
            }).goAway(1200);
        }

    }
})

App.vue

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />

    <div v-show="jwt !== null">
      <p>Returned JWT: {{jwt}}</p>
      <p>
        <a @click="logOut" href="#">Log Out</a>
      </p>
    </div>

    <form class="form-signin" @submit.prevent="login" v-show="jwt == null">
      <label for="inputUsername">Username</label>
      &nbsp;
      <input
        v-model="username"
        type="text"
        id="inputUsername"
        placeholder="Username"
        required
        autofocus
      />
      <br />
      <br />
      <label for="inputPassword">Password</label>
      &nbsp;
      <input
        v-model="password"
        type="password"
        id="inputPassword"
        placeholder="Password"
        required
      />
      <p>
        <input type="submit" value="Sign In" />
      </p>
    </form>
  </div>
</template>


<script>
export default {
  name: "vue",
  components: {},
  data() {
    return {
      username: "admin",
      password: "passwd",
      jwt: null
    };
  },
  sockets: {
    auth: function(response) {
      this.jwt = response.jwt;
    }
  },
  methods: {
    login() {
      if (this.$socket.disconnected) {
      this.$toasted.global
        .appError({
          message: "You are not connected to the server!"
        })
        .goAway(1200);
      } else {
      this.$socket.emit("authenticate", {
        data: JSON.stringify({
          username: this.username,
          password: this.password
        })
      });
      }
    },
    logOut() {
      this.jwt = null;
      this.$toasted.global
        .appSuccess({
          message: "Locally Logged Out!"
        })
        .goAway(1200);
    }
  }
};
</script>

Top comments (2)

Collapse
 
boomnarak profile image
boomnarak • Edited

sockets: {
auth: function(response) {
this.jwt = response.jwt;
}
},

hello, I have some question. it can't listener any (auth: function () ...) response from server but in vuex it work. please tell me what is the problem.

Collapse
 
boomnarak profile image
boomnarak • Edited

I can find that root cause. it work for me when I used npm i vue-socket.io@3.0.7