Search code examples
jquerynode.jssocket.iovue.jskoa2

Vue.js + Socket.io - append an element in a real time app?


I'm new to Vue.js, so wonder how this can be done in Vue instead jQuery?

The socket.io and jQuery will append a <li> each time when someone has typed something on the browser.

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <script src="/socket.io/socket.io.js"></script>
  <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
    <script>
        $(function () {
            var socket = io();
            $('form').submit(function(){
              socket.emit('chat message', $('#m').val());
              $('#m').val('');
              return false;
            });

            socket.on('chat message', function(msg){
              $('#messages').append($('<li>').text(msg));
            });
          });
    </script>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

How is this done in Vue?

I tried:

     $(function () {
        var socket = io();
        $('form').submit(function(){
          socket.emit('chat message', $('#m').val());
          $('#m').val('');
          return false;
        });

        var app = new Vue({
          el: '#app',
          data: {
            message: "Hello World"
          },
          created: function() {
            socket.on('chat message', function(data) {
                this.message = data.message;
            }.bind(this));
          }
        });
      });

in my new HTML:

<form action="">
  <input id="m" autocomplete="off" /><button>Send</button>
  <span id="app">{{ message }}</span>
</form>

It does not work obviously.

Any ideas?


Solution

  • You can do it the complete vuejs way even without using jquery like this:

    html

    <div id="app">  
        <form @submit.prevent="submitMsg">
          <input id="m" autocomplete="off" v-model="inputMsg"/>
          <button type="submit">Send</button>
          <span>
              <ul>
                  <li v-for="message in messages">{{message}}</li>
              </ul>
          </span>
        </form> 
    </div> 
    

    script

    var app = new Vue({
          el: '#app',
          data: {
              socket: null,
              inputMsg: '',
              messages: []
          },
          created: function() {
            this.socket = io();
            this.socket.on('chat message', function(msg) {
                this.messages.push(msg);
            }.bind(this));
          },
          methods:{
              submitMsg(){
                   this.socket.emit('chat message', this.inputMsg);
                   this.inputMsg = '';
              }
          }
        });
    

    So what''s happening:

    • Wrapped the whole html with a div of id='app' so that it is controlled by vue

    • added a submit event on the form using @submit.prevent which calls the method submitMsg. The prevent modifier prevents form from being actually submitting

    • setup a v-model on the input which is two way data boun

    • now you can use the v-model which is inputMsg to get the value of the input and use it in the form submission method

    • after emitting the socket event setting the input value empty using this.inputMsg = '';

    • in crated lifecycle hook setup a socket event listener and push message to the data property messages:[] which is initialized as an empty array

    • loop trough the messages[] in your template using v-for='message in messages'which renders a <li> element for every item in messages[]