Building a chat application

In this recipe, we'll combine what you learned with initializers and dependency injection to create a chat room. The chat room will use WebSockets to communicate with the host.

How to do it...

  1. In a new application, generate these files:
    $ ember g service sockjs
    $ ember g component chat-room
    $ ember g initializer application
    $ bower install sockjs --save
    

    These will generate the files needed for the project. The chat-room component will have all the logic for the chat room that we'll create.

  2. Import the SockJS library to the application:
    // ember-cli-build.js
    …
    app.import('bower_components/sockjs/sockjs.min.js');
    …

    This will import the library so that we can use the global variable, sockjs, anywhere in our application.

  3. Create a new service for SockJS:
    // app/services/sockjs.js
    /* global SockJS */
    import Ember from 'ember';
    const {run} = Ember;
    
    export default Ember.Service.extend(Ember.Evented,{
        socket: null,
        init() {
          this._super();
          let socket = new SockJS('http://localhost:7000');
          socket.addEventListener('message', run.bind(this, (event)=> {
            this.trigger('messageReceived', event.data);
            console.log(event.data);
          }));
          this.set('socket',socket);
        },
        sendInfo(message) {
          this.get('socket').send(message);
    
        }
    
    });

    Let's take a look at this in more detail:

    /* global SockJS */

    This line is needed so that JSHint won't complain about the SockJS global variable. JSHint is the built-in library for Ember CLI that detects errors in your program:

    export default Ember.Service.extend(Ember.Evented,{

    This adds the Ember.Evented mixin to the service. This mixin allows Ember objects to subscribe and emit events. This is perfect for what we need to do in this example:

    init() {
        this._super(…arguments);
    },

    The init method is where the SockJS socket will be set up and the event listener will be created. This method will fire after the service is initialized. The this._super method guarantees that the init method is set up properly:

    let socket = new SockJS('http://localhost:7000');

    The preceding line creates a new socket server at the localhost port 7000:

    socket.addEventListener('message', run.bind(this, (event)=> {
        this.trigger('messageReceived', event.data);
        console.log(event.data);
    }));
    this.set('socket',socket);

    This creates an event listener that is fired when a message is received. The run.bind method is a part of the Ember run loop that we'll describe later in this chapter. This ensures that all the requests are taken care of properly in the run loop.

    The this.trigger is a part of the Event.Evented class. The trigger method creates a new event called messageReceived. We can subscribe to this event so that other methods in Ember can be triggered when a message is received. Finally, we log the information in event.data to the console and set the socket property:

        sendInfo(message) {
          this.get('socket').send(message);
        }

    This method accepts message and sends it the socket server we defined earlier. The socket property is accessed here.

  4. Inject the new service into all the components in the application:
    // app/initializers/application.js
    export function initialize( application ) {
        application.inject('component', 'sockjs', 'service:sockjs');
    }
    
    export default {
        name: 'websockets',
        initialize
    };

    The initializer takes the service called sockjs and injects it into all the components. This will be run whenever the program first boots. We use this so that we don't have to specifically inject the sockjs service into each component.

  5. Create a new component for the chat room:
    // app/components/chat-room.js
    import Ember from 'ember';
    const {$} = Ember;
    
    
    export default Ember.Component.extend({
        message: '',
    
        init() {
          this._super(…arguments);
          this.sockjs.on('messageReceived',this, 'messageReceived');
        },
    
        messageReceived(message){
          $('#chat-content').val((i, text)=>
          `${text}${message}
    `;
          );
          this.set('message',message);
        },
        actions: {
          enter(info,username) {
            this.sockjs.sendInfo(`${username}: ${info}`);
    
          }
        }
    
    });

    Let's break this down into smaller parts:

        init() {
          this._super(…arguments);
          this.sockjs.on('messageReceived',this, 'messageReceived');
        },

    This init method fires on initialization and sets up the component. We can then subscribe to the event that we created earlier in the service using on. The first parameter is the name of the event. The second is the binding. The last is the name of the callback function. Therefore, in this example, whenever a message is received in the service, the messageReceived callback in this component will be fired:

        messageReceived(message){
          $('#chat-content').val((i, text)=>
          `${text}${message}
    `
          );
          this.set('message',message);

    This is the messageReceived callback. It uses a little bit of jQuery to find the chat-content ID and concatenate the existing message to it using ES6 string interpolation. In addition, the message property is set:

        actions: {
          enter(info,username) {
            this.sockjs.sendInfo(`${username}: ${info}`);
    
          }
        }

    This action sends info and username to the socket. This way, any other clients connected will be notified.

  6. Create the chat-room.hbs template file for the component:
    // app/templates/components/chat-room.hbs
    
        <textarea id="chat-content" style="width:500px;height:300px" ></textarea><br/>
        {{input type='text' placeholder='User Name' value=uname}}
        {{input type='text' placeholder='Chat Message' value=mess}}
        <button {{action 'enter' mess uname}}>Send</button><br>
    
    Message received:{{message}}

    This code displays the messages from the server. The input helpers capture the username and message. Each value is passed to the enter action when the Send button is clicked.

  7. Add the component to the application.hbs file:
    // app/templates/application.hbs
    <h2 id="title">Welcome to Ember</h2>
    
    {{outlet}}
    {{chat-room}}

    This adds the component to the application.

  8. Start the node server. Then start the Ember application. You'll see the following screen:
    How to do it...

    As each client connects they'll be able to send messages to the server. Each client will receive these messages and display them in the chat box.

How it works...

A chat room consists of multiple clients talking to a server. The server's job is to notify all the other clients connected when a messages is received. This is done in this example using SockJS with WebSockets. The SockJS library has message events that we can set up in Ember. When a message is received, it is then sent to a component that updates its template with the message.

There's more...

To use the preceding example, you'll need to set up a WebSocket server. Here are the steps to create a simple Node.js SockJS server. To learn more about SockJS, check out their GitHub page at https://github.com/sockjs/sockjs-node.

  1. In a new directory, run the npm init command:
    $ npm init
    $ npm install sockjs –save
    

    This will generate the package.json file and install the SockJS server in it.

  2. Create a new app.js file for the WebSocket server:
    // app.js
    var http = require('http');
    var sockjs = require('sockjs');
    
    
    var clients = {};
    
    function broadcast(message){
        for (var client in clients){
          clients[client].write(message);
        }
    }
    
    var socketServer = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
    socketServer.on('connection', (conn)=> {
        clients[conn.id] = conn;
    
        conn.on('data', (message)=> {
          console.log('received ' + message);
          broadcast(message);
        });
        conn.write("hello from the server thanks for connecting!");
        conn.on('close', ()=> {
          delete clients[conn.id];
        });
        console.log("connected");
    });
    
    var server = http.createServer();
    socketServer.installHandlers(server);
    server.listen(7000, '0.0.0.0');

    This server uses the SockJS library to create a new socket server. When a new client connects, it's added to an array. When it receives data, it broadcasts this data to all the other servers connected using this function:

    function broadcast(message){
        for (var client in clients){
          clients[client].write(message);
        }
    }

    This function sends a broadcast message to every other client connected with the message it just received. When Ember receives this information, it's written to the chat box.

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

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