Message list
We can display a list of messages on screen by creating a new component with a prop that accepts an array of messages. Create a new component inside the components folder namedMessageList.vue:
<template>
<div>
<span v-for="message in messages" :key="message.id">
<strong>{{message.username}}: </strong> {{message.message}}
</span>
</div>
</template>
<script>
export default {
props: ['messages'],
};
</script>
<style scoped>
div {
overflow: scroll;
height: 150px;
margin: 10px auto 10px auto;
padding: 5px;
border: 1px solid gray;
}
span {
display: block;
padding: 2px;
}
</style>
This component is fairly simple; all it does is iterate over ourmessagesarray using thev-fordirective. We pass the messages array into this component using the appropriate prop.
Instead of registering this component globally, let's register it specifically inside of ourApp.vuecomponent. Whilst we're here, we can also add some dummy data to themessagesarray:
import MessageList from './components/MessageList.vue';
export default {
data() {
return {
isConnected: false,
messages: [
{
id: 1,
username: 'Paul',
message: 'Hey!',
},
{
id: 2,
username: 'Evan',
message: 'How are you?',
},
],
};
},
components: {
MessageList,
},
We can then add themessage-listcomponent to our template:
<div class="container">
<message-list :messages="messages" />
</div>
We're passing the messages in as a prop based on the messages array found inside of our data object. We can also add the following styles:
<style>
.container {
width: 300px;
margin: 0 auto;
}
</style>
Doing so will center our message box on the screen and constrain thewidthfor demonstration purposes.
We're making progress! Here's our message box:
What next? Well, we still need the ability to add messages to our list. Let's work on that next.
Adding messages to the list
Create a new component inside of the components folder namedMessageForm.vue. This will serve to input messages into the list.
We can start off with the following:
<template>
<form @submit.prevent="sendMessage">
<div>
<label for="username">Username:</label>
<input type="text" name="username" v-model="username">
</div>
<div>
<label for="message">Message:</label>
<textarea name="message" v-model="message"></textarea>
</div>
<button type="submit">Send</button>
</form>
</template>
<script>
export default {
data() {
return {
username: '',
message: '',
};
},
};
</script>
<style>
input,
textarea {
margin: 5px;
width: 100%;
}
</style>
This essentially allows us to capture user input for both the selectedusernameandmessage. We can then use this information to send data to ourSocket.ioserver within thesendMessagefunction.
By [email protected] our form rather than@submit, we're ensuring that we override the default behavior of the submitted form; this is necessary or else our page would otherwise reload.
Let's go and register our form inside ofApp.vue, even though we haven't hooked up any actions yet:
import MessageList from './components/MessageList.vue';
export default {
// Omitted
components: {
MessageList,
MessageForm,
},
}
We can then add this to our template:
<template>
<div>
<navigation-bar />
<div class="container">
<message-list :messages="messages" />
<message-form />
</div>
<connection-status :isConnected="isConnected" />
</div>
</template>
Here's what our application looks like now:
Server-side events with Socket.io
In order to send a new message, we can listen for an event namedchatMessagewithin ourserver.jsfile.
This can be done inside of our original connection event, ensuring that we listen for events on a socket-by-socket basis:
io.on('connection', socket => {
console.log('A user connected.');
socket.on('chatMessage', message => {
console.log(message);
})
});
If we then send thechatMessageevent from our client, it should subsequently log out this message inside of our Terminal. Let's try it out!
Because we've made a change to ourserver.jsfile, we'll need to restart the Node instance. HitCTRL+_C_within the Terminal window that is runningserver.jsand run nodeserver.jsagain.
Nodemon
Alternatively, you may want to use a module callednodemonto automatically do this whenever any changes are made.
Run the following inside of your Terminal:
# Install nodemon globally
$ npm install nodemon -g
We can then run:
# Listen for any changes to our server.js file and restart the server
$ nodemon server.js
Great! Let's go back to ourMessageFormcomponent and create thesendMessagefunction:
methods: {
sendMessage() {
this.socket.emit('chatMessage', {
username: this.username,
message: this.message,
});
},
},
At this point hitting Send doesn't add the message to an array yet, but it does give us the sent message inside of our Terminal! Let's take a look:
As it turns out, we don't have to write much more code to take advantage of our WebSockets. Let's head back over to theApp.vuecomponent and add a function to our sockets object calledchatMessage. Notice how this is identical to the event name, meaning that every time this event is fired we can run a particular method:
export default {
// Omitted
sockets: {
connect() {
this.isConnected = true;
},
disconnect() {
this.isConnected = false;
},
chatMessage(messages) {
this.messages = messages;
},
},
}
Our client-side code is now hooked up and listening to thechatMessageevent. The problem is that our server-side code isn't currently sending anything to the client! Let's fix this by emitting an event from within the socket:
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const PORT = 3000;
http.listen(PORT, () => console.log(`Listening on port: ${PORT}`));
const messages = [];
const emitMessages = () => io.emit('chatMessage', messages);
io.on('connection', socket => {
console.log('A user connected.');
emitMessages(messages);
socket.on('chatMessage', message => {
messages.push(message);
emitMessages(messages);
});
});
We're keeping the messages in memory with an array named messages. We're also then emitting those messages downstream whenever a client connects to our application (all the previous messages will be shown). As well as this, any time there is a new message added to the array, we're also sending this to all of the clients too.
If we open up two Chrome tabs we should then be able to have a self-directed conversation!
We can then talk to ourselves in the other tab!
Summary
In this chapter, we learned how to create HTTP requests with Vue using theAxioslibrary andjson-server. This allows us to interact with third-party APIs and power up our Vue applications.
We also looked at how to create a larger application using WebSockets andSocket.io. This allows us to have real-time communication with other clients that are connected to our application, allowing for even more possibilities.
We've come a long way! In order to truly take advantage of Vue, we'll need to master the router and look at advanced state management concepts. That is all coming up in the next chapter(s)!