Creating the ws-client Module

The ws-client.js module will handle communicating with your Node WebSocket server.

It will have four responsibilities:

  • connecting to the server

  • performing initial setup when the connection is first opened

  • forwarding incoming messages to their handlers

  • sending outgoing messages

Check out how those responsibilities relate to your other components (Figure 17.14).

Figure 17.14  ws-client’s interfaces

ws-client’s interfaces

As you build out your client, you will get a tour of some new ES6 features as well.

Connection handling

First, build out your collection handling. Begin by opening ws-client.js and declaring a variable for the WebSocket connection.

let socket;

This declaration uses a new way of defining variables in ES6 called let scoping. If you use let scoping to declare a variable – using the keyword let instead of var – your variable will not be hoisted.

Hoisting means that the variable declarations get moved to the top of the function scope in which they are created. This is something that the JavaScript interpreter does behind the scenes. Unfortunately, it can lead to hard-to-find errors.

You will read more about hoisting at the end of the chapter. For now, know that let is a safer way to declare variables in if/else clauses and in the body of loops.

Now, add a method to ws-client.js to initialize your connection.

let socket;

function init(url)  {
  socket = new WebSocket(url);
  console.log('connecting...');
}

The init function connects to the WebSockets server. Next, you want to wire up ws-client.js to ChatApp in app.js.

To be a functioning module, ws-client.js needs to specify what it exports. You need to export a single value: an object code with the exported functions as its properties. You are going to use the same export default syntax that you used at the beginning of the chapter – plus an additional bit of ES6 handiness.

Add the export to the end of ws-client.js, as shown.

...
function init(url)  {
  socket = new WebSocket(url);
  console.log('connecting...');
}

export default {
  init,
}

Notice that you did not have to specify the property names. This syntactic shortcut is the equivalent of:

export default {
  init: init
}

If the key and value have the same name, ES6 allows you to omit the colon and the value. The key will automatically be the variable name, and the value will automatically be the value associated with that name. This feature of ES6 is the enhanced object literal syntax.

Now that you have the ws-client module set up, it is time to import the values it provides in app.js. Begin by adding an import statement to the top of app.js:

import socket from './ws-client';

class ChatApp {
  constructor() {
    console.log('Hello ES6!');
  }
}
...

socket will be the object you exported from ws-client.js.

Next, in the ChatApp constructor, call socket.init with the URL of your WebSocket server.

import socket from './ws-client';

class ChatApp {
  constructor() {
    console.log('Hello ES6!');
    socket.init('ws://localhost:3001');
  }
}
...

Your npm script should rebuild the code for you. (You may need to restart npm run watch and npm run dev in separate windows, if you have let one or both of them stop.) Reload your browser and you should see 'connecting...' logged to the console, as shown in Figure 17.15.

Figure 17.15  Message logged on WebSocket initialization

Message logged on WebSocket initialization

With that, you have the skeleton of your app up and running.

Handling events and sending messages

When your App module calls init, a new WebSocket object is instantiated and a connection is made to the server. But your App module needs to know when this process has completed so that it can do something with the connection.

The WebSocket object has a set of special properties for handling events. One of these is the onopen property. Any function assigned to this property will be called when the connection to the WebSocket server is made. Inside this function, you can carry out any steps that need to be made upon connecting.

In order for the ws-client module to be flexible and reusable, you will not hardcode the steps that the App module needs to make upon connecting. Instead, you will use the same pattern you used for registering click and submit handlers in CoffeeRun.

Add a function called registerOpenHandler to ws-client.js. registerOpenHandler will accept a callback, assign a function to onopen, and then invoke the callback inside the onopen function.

let socket;

function init(url)  {
  socket = new WebSocket(url);
  console.log('connecting...');
}

function registerOpenHandler(handlerFunction) {
  socket.onopen = () => {
    console.log('open');
    handlerFunction();
  };
}
...

This function definition is different from what you have written before. This is a new ES6 syntax called an arrow function. Arrow functions are a shorthand for writing anonymous functions. Apart from being a bit easier to write, arrow functions work exactly the same as anonymous functions.

registerOpenHandler takes a function argument (handlerFunction) and assigns an anonymous function to the onopen property of the socket connection. Inside of this anonymous function, you call the handlerFunction that was passed in.

(Using an anonymous function is more complicated than writing socket.onopen = handlerFunction. This pattern will serve you well when you need to respond to an event but have intermediary steps that must happen before forwarding it on – like writing a log message, as you have done here.)

Next, you need to write an interface for handling messages as they come in over your WebSockets connection. Write a new method called registerMessageHandler in ws-client.js. Assign an arrow function to the socket’s onmessage property; this arrow function should expect to receive an event argument.

...
function registerOpenHandler(handlerFunction) {
  socket.onopen = () => {
    console.log('open');
    handlerFunction();
  };
}

function registerMessageHandler(handlerFunction) {
  socket.onmessage = (e) => {
    console.log('message', e.data);
    let data = JSON.parse(e.data);
    handlerFunction(data);
  };
}
...

Arrow function parameters go inside the parentheses, just as they do for regular functions.

The Chattrbox client receives an object from the server in its onmessage callback inside registerMessageHandler. This object represents the event and has a data property that contains the JSON string from the server. Each time you receive a string, you convert the string to a JavaScript object. You then forward it along to handlerFunction.

The last bit is the piece that will actually send the message to your WebSocket. Write this in ws-client.js as a function called sendMessage. You will do this in two parts. First, you will turn your message payload (containing the message, the username, and the timestamp) into a JSON string. Then you will send that JSON string to the WebSocket server.

...
function registerMessageHandler(handlerFunction) {
  socket.onmessage = (e) => {
    console.log('message', e.data);
    let data = JSON.parse(e.data);
    handlerFunction(data);
  };
}

function sendMessage(payload) {
  socket.send(JSON.stringify(payload));
}
...

Finally, add exports for your new methods using the enhanced object literal syntax.

...
function sendMessage(payload) {
  socket.send(JSON.stringify(payload));
}

export default {
  init,
  registerOpenHandler,
  registerMessageHandler,
  sendMessage
}

With that, ws-client.js has everything it needs to communicate back and forth with the server. Your last job in ws-client.js will be to test it by sending a message.

Sending and echoing a message

Update the ChatApp constructor in app.js. After calling socket.init, call registerOpenHandler and registerMessageHandler, passing them arrow functions.

import socket from './ws-client';

class ChatApp {
  constructor() {
    socket.init('ws://localhost:3001');
    socket.registerOpenHandler(() => {
      let message = new ChatMessage({ message: 'pow!' });
      socket.sendMessage(message.serialize());
    });
    socket.registerMessageHandler((data) => {
      console.log(data);
    });
  }
}
...

When the connection is open, you are immediately sending a dummy message. And when a message is received, you are logging it to the console.

Save your code and reload the browser when the build process finishes. You should see that a message was sent and echoed back (Figure 17.16).

Figure 17.16  Call and response with WebSockets

Call and response with WebSockets

Excellent work! You have two of the three primary modules for Chattrbox working. You will finish Chattrbox in the next chapter by creating a module that connects your existing modules to the UI. This module will draw new messages to the message list and send messages when the form is submitted.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.220.16