Adding the ChatMessage Class

Chatting between two terminals is fun (and makes you look cool at the coffee shop), but it is time to upgrade to sending messages from browser to browser. You will write a helper class that handles constructing and formatting message data.

There are three pieces of information you will want to track for each message. You need to know the text of the message, who sent it, and at what time.

JavaScript Object Notation – more commonly known as JSON (pronounced “Jason,” per creator Douglas Crockford) – is a lightweight data-interchange format. You have already been using JSON for your package.json file. It is human readable and language independent, and it is ideal for sending and receiving the kind of data you want to exchange with Chattrbox.

Here is a sample message formatted as JSON:

{
  "message": "I'm Batman",
  "user": "batman",
  "timestamp": 614653200000
}

Chattrbox message data will come from two different sources. One source is in the client, when the user fills out the form. The other source is the server, when the message is sent over a WebSocket connection to other clients.

When message data comes from the form, you will need to add the username and timestamp before sending it to the server. When the data comes from the server, all three pieces of information should be included. How should you handle this discrepancy? There are a number of options. Let’s briefly look at a few of them, including some that take advantage of some handy ES6 features.

Create a class to represent individual chat messages in app.js.

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

class ChatMessage {
  constructor(data) {
  }
}

export default ChatApp;

The first way to approach the problem is a simple constructor that accepts the message text, username, and timestamp. (Do not make this change in your file. It is only an example.)

...
class ChatMessage {
  constructor(message, user, timestamp) {
    this.message = message;
    this.user = user || 'batman';
    this.timestamp = timestamp || (new Date()).getTime();
  }
}
...

You have seen this pattern a number of times. You assign the parameter values to instance properties, providing fallbacks for username and timestamp using the || operator.

This is fine, but ES6 gives you a more compact way to write this same pattern using default arguments.

...
class ChatMessage {
  constructor(message, user='batman', timestamp=(new Date()).getTime()) {
    this.message = message;
    this.user = user;
    this.timestamp = timestamp;
  }
}
...

This syntax makes it obvious which values must be passed in and which ones are optional. You can see that only the message argument is mandatory. The others have defaults.

This version of the constructor can handle messages received from the server or created by the form. But it requires that the caller know the order of arguments, which can get cumbersome for functions and methods that have three or more arguments.

An alternative to this is for the constructor to receive a single object as argument, with the key/value pairs specifying the values for message, user, and timestamp. For that, you can use the destructuring assignment syntax.

...
class ChatMessage {
  constructor({message: m, user: u, timestamp: t}) {
    this.message = m;
    this.user = u;
    this.timestamp = t;
  }
}
...

Destructuring may look a little odd, but here is how it works. You call the constructor like this:

new ChatMessage({message: 'hello from the outside',
                 user='[email protected]', timestamp=1462399523859});

The destructuring syntax looks for the key message in the argument. It finds the value 'hello from the outside' and assigns it to a new local variable m. This variable can then be used inside the body of the constructor. The same thing happens for the username and timestamp properties.

But with this syntax you lose the convenience of the default parameters. Luckily, you can combine the two techniques. This final version of the constructor is the one you should add to app.js:

...
class ChatMessage {
  constructor(data){
    message: m,
    user: u='batman',
    timestamp: t=(new Date()).getTime()
}) {
    this.message = m;
    this.user = u;
    this.timestamp = t;
  }
}
...

In this version, you are plucking values out of the object that is passed to the constructor. For any values that do not exist, defaults are provided.

While default arguments can only exist as part of a function (or constructor) definition, destructuring can be used as part of an assignment. You might also write the constructor like this:

...
class ChatMessage {
  constructor(data) {
    var {message: m, user: u='batman', timestamp: t=(new Date()).getTime()} = data;
    this.message = m;
    this.user = u;
    this.timestamp = t;
  }
}
...

OK, the detour is over. Time to get back to building Chattrbox!

Your ChatMessage class stores all of the important information as properties, but its instances also inherit ChatMessage’s methods and other information. That makes ChatMessage instances unsuitable for sending through WebSockets. A stripped-down version of that information is necessary.

Write a serialize method in app.js to represent the data in ChatMessage’s properties as a plain JavaScript object.

...
class ChatMessage {
  constructor({
    message: m,
    user: u='batman',
    timestamp: t=(new Date()).getTime()
  }) {
    this.user = user;
    this.message = message;
    this.timestamp = timestamp;
  }
  serialize() {
    return {
      user: this.user,
      message: this.message,
      timestamp: this.timestamp
    };
  }
}

export default ChatApp;

Your ChatMessage class is now ready for use. It is time to move on to the next module for Chattrbox.

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

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