Build a live comments feature with Ember.js

Introduction

Introduction

We are in 2018, users of your web application don’t have the luxury of time to wait for a page refresh before getting an update or properly interacting when carrying out a basic activity as posting a comment irrespective of the web technology of your choice.

If you are like me and very passionate about properly improving interactivity and engaging your users, then this post is for you!

Together in this tutorial, we’ll build a simple realtime commenting system where users can add comments to a blog post and see the update in realtime. In addition to the realtime functionality, we will also use a technique called sentiment analysis to determine the attitude of users based on the context of the received post. The application will be built using Ember.js and powered by Pusher.

Here is what the application will look like once we are done:

ember-comments-demo

Prerequisites

It is important that you have Node.js and npm installed on your machine in order to properly run the basic setups, learn how to install Node.js and npm here. A quick run-down of the core technologies we will be using include:

  • Ember.js: a productive frontend framework built for ambitious web developers.
  • Pusher: a Node.js client to interact with the Pusher REST API
  • Express: Node utility for handling HTTP requests via routes
  • Axios: a promise-based HTTP client that works both in the browser and in a Node.js environment.
  • Body Parser: attaches the request payload on Express’s req, hence req.body stores this payload for each request.
  • Sentiment: Sentiment is a module that uses the AFINN-165 wordlist and Emoji Sentiment Ranking to perform sentiment analysis on arbitrary blocks of input text.

Setting up the project

The most convenient and recommended way of getting an Ember app up and running with a single command is by using the Ember CLI tool. You can install it with npm, open your terminal and type this command:

     npm install -g ember-cli

The Ember CLI will give us access to the ember command that will be used to set up our project easily.

Creating the comment application

We can now proceed to create our application by using the ember new command. Open up the terminal on your machine and run a command that will create an application named live-comments-ember in your project directory or any location specified:

     ember new live-comments-ember

Next, change directory into the newly created project and start the development server:

1// change directory
2     cd live-comments-ember
3     
4     // start the development server
5     ember serve

This will start the development server on http://localhost:4200. Open your favorite browser and navigate to that URL, you should see a default welcome page by Ember.js as shown below:

ember-start-page

Installing server dependencies

Run the following command to install the dependencies required for this project using:

1npm install --save axios pusher pusher-js
2    npm install --save body-parser cors dotenv express sentiment
3    ember install ember-browserify

Pusher account setup

Head over to Pusher and sign up for a free account.

ember-pusher-signup

Create a new app by selecting Channels apps on the sidebar and clicking Create Channels app button on the bottom of the sidebar:

ember-pusher-create-app

Configure an app by providing basic information requested in the form presented. You can also choose the environment you intend to integrate Pusher with to be provided with some boilerplate code:

ember-pusher-config-app

You can retrieve your keys from the App Keys tab:

ember-pusher-app-keys

Setting the environment variables

Create a .env file in the root directory of your application and add your application credentials as obtained from your Pusher dashboard as follows:

1PUSHER_APP_ID=YOUR_APP_ID
2      PUSHER_APP_KEY=YOUR_APP_KEY
3      PUSHER_APP_SECRET=YOUR_APP_SECRET_KEY
4      PUSHER_APP_CLUSTER=CLUSTER

We will make use of the variables specified in the above snippet later in our project. And do ensure that you replace YOUR_APP_ID, YOUR_APP_KEY, YOUR_APP_SECRET_KEY and CLUSTER placeholders with the appropriate credentials.

Setting up the server

This application needs to be able to process a POST request with a new comment as submitted by the users. So we’ll set up a simple server that will receive the comment and publish it to a designated channel for the client side to listen and subscribe to. We’ll set it up by using Node and Express and run it on http://localhost:3000.

Since the Ember application that we configured earlier is running on a different domain, we’ll enable CORS in order to ensure communication between the Express server and Ember.

Create a app.js file in the root directory of your application and add the following code snippet to it to set up the server:

1// app.js
2    
3      const express = require('express');
4      const bodyParser = require('body-parser');
5      const cors = require('cors');
6      const Pusher = require('pusher');
7      const Sentiment = require('sentiment');
8      require('dotenv').config();
9      const app = express();
10      app.use(cors());
11      app.use(bodyParser.urlencoded({extended: false}));
12      app.use(bodyParser.json());
13      const pusher = new Pusher({
14          appId: process.env.PUSHER_APP_ID,
15          key: process.env.PUSHER_APP_KEY,
16          secret: process.env.PUSHER_APP_SECRET,
17          cluster: process.env.PUSHER_APP_CLUSTER,
18          encrypted: true
19      });
20      app.set('port', process.env.PORT || 3000);
21      app.post('/posts', (req, res) => {
22          const sentiment = new Sentiment();
23          const sentimentScore = sentiment.analyze(req.body.text).score;
24          const payload = {
25              text: req.body.text,
26              sentiment: sentimentScore
27          }
28          pusher.trigger('chat', 'post', payload);
29          res.send(payload)
30      })
31      app.listen(app.get('port'), () => {
32          console.log("Listening at " + app.get('port'));
33      })

Here we loaded all the necessary middlewares for the express server and then configure Pusher using the credentials we added to our environment variables earlier.

Furthermore, we also created the /posts endpoint that will receive and process comments sent in from our Ember app. Finally, we trigger an event named post to Pusher Channels on a channel called chat. The chat channel holds a payload that contains the comment posted by the user and the overall sentiment score of the comment.

Open another terminal and run the command below to start the server on http://localhost:3000

    node app.js

This will log a message to the console stating that Node is running on port 3000.

Next, we’ll create components required for our application.

Creating the UI components

Ember supports the usage and sharing of UI elements on multiple pages. We will leverage on this and generate components for separate parts of our application. The components required for our application are:

  • comments-app: this will encompass the entire application
  • comment-post: this component will display each comment submitted by users in realtime
  • list-``posts: this component will list all the existing comments

The Ember component generally consist of two parts: a JavaScript component file that defines behavior and a Handlebars template that defines the markup for the component’s UI.

Comment app component

We’ll start by generating a component for the comments-app, by using the command below:

    ember g component comments-app

Comments app template

This component will basically be used to display the sample post and all the comments submitted by readers. Locate ./app/templates/components/comments-app.hbs and update it as shown below:

1{{!-- ./app/templates/components/comments-app.hbs --}}
2    
3      <div class="row comment-box">
4          <div class="col-md-6 col-md-offset-3">
5               <div class="sample-post">
6                 <p>Does your team know which product feature really makes a difference 
7                  for customers? Monitor industry and customer
8                  trends on a broad scale with our advanced sentiment technology 
9                  to develop products that succeed.
10                 </p>
11              </div>
12          </div>
13      </div>
14      <div class="row comment-box">
15         <div class="col-md-6 col-md-offset-3">
16            <div class="panel panel-white post panel-shadow">
17                 {{#list-posts}}{{/list-posts}}
18            </div>
19         </div>
20      </div>

Comment post component

Next, we’ll create a component to render each comment once submitted. Open your terminal and run this command:

      ember g component comment-post

Comment post component template

The comment-post template file will display individual comment with emojis to represent the mood of the users. Add the following content to your ./app/templates/components/comment-post.hbs file:

1{{!-- ./app/templates/components/comment-post.hbs --}}
2      
3      <div class="post-heading">
4          <div class="pull-left image">
5            <img src="http://bootdey.com/img/Content/user_1.jpg" class="img-circle avatar" alt="user profile image">
6          </div>
7          <div class="pull-left meta">
8                  <div class="title h5">
9                   <a href="#"><b> Anonymous </b></a>
10                     posted a comment.
11                  </div>
12            <h6 class="text-muted time">{{ comment.mood }}</h6>
13             <p>{{comment.text}}</p>
14          </div>
15      </div>

List posts component

The last component required here is list-``posts. As stated earlier, it will be used to list all the existing comments submitted by readers. Run the command below to generate it:

     ember g component list-posts

List posts component file

Add the following content to ./app/components/list-posts.js:

1// ./app/components/list-posts.js
2      
3      import Component from '@ember/component';
4      import axios from 'npm:axios';
5      import Pusher from 'npm:pusher-js';
6      
7      const SAD_EMOJI = [55357, 56864];
8      const HAPPY_EMOJI = [55357, 56832];
9      const NEUTRAL_EMOJI = [55357, 56848];
10      
11      export default Component.extend({
12          comments: null,
13          init() {
14              this._super(...arguments);
15              this.set('comments', []);
16              let pusher = new Pusher('YOUR_APP_KEY', { // update your APP_KEY
17                  cluster: 'CLUSTER',
18                  encrypted: true
19              });
20              const channel = pusher.subscribe('chat');
21              channel.bind('post', data => {
22                  const analysis = data.sentiment > 0 ? HAPPY_EMOJI : (data.sentiment === 0 ? NEUTRAL_EMOJI : SAD_EMOJI);
23                  const response = {
24                      text: data.text,
25                      mood: String.fromCodePoint(...analysis)
26                  }
27                  this.get('comments').pushObject(response);
28              });
29          },
30          actions: {
31              newPost() {
32                  const text = this.get('newPost');      
33                  axios.post('http://localhost:3000/posts', { text });
34                  this.set('newPost', '');
35              }
36          }
37      });

First, we imported the required modules for this component and we added some constants of code points for a particular sentiment emoji. This will help us rendered an emoji corresponding to the mood of the user as analyzed by the sentiment module once a comment is submitted.

Next, we initialize Pusher with the APP_KEY and CLUSTER as obtained from our Pusher account dashboard and then proceeded to use the subscribe() method from Pusher to subscribe to the created chat channel.

Finally, the newPost() action basically receives the comment submitted by a user and POST it to the server. This action will be triggered once the Post Comment button in the template file for this component is clicked.

Don’t forget to replace the YOUR_APP_KEY and CLUSTER placeholder with the appropriate details from your Pusher account dashboard.

List posts component template

Here in the template file for list``-posts component, we’ll loop over each comment and render a comment-post component. Open ./app/templates/components/list-posts.hbs and edit as shown below:

1{{!-- ./app/templates/components/list-posts.hbs --}}
2    
3      {{#each comments as |comment|}}
4      <div class="row msg_container base_receive">
5            {{#comment-post comment=comment}}{{/comment-post}}
6      </div>
7      {{/each}}
8      
9      <div class="footer">
10          <div class="input-group">
11            {{ input value=newPost id="btn-input" type="text" class="form-control input-sm" placeholder="Write a comment"}}
12            <span class="input-group-btn">
13                <button class="btn btn-primary btn-sm" id="btn-chat" {{action "newPost"}}>Post Comment</button>
14            </span>
15         </div>
16      </div>

Update the app template

Update the application template with:

1{{!-- ./app/templates/application.hbs --}}
2      
3      {{#comments-app}}{{/comments-app}}
4      {{outlet}}

Stylesheet

To add some minimal styling to the page, open ./app/styles/app.css and paste this code in it:

1// ./app/styles/app.css
2    
3      .post .post-heading {
4        height: 95px;
5        padding: 20px 15px;
6      }
7      .post .post-heading .avatar {
8        width: 60px;
9        height: 60px;
10        display: block;
11        margin-right: 15px;
12      }
13      .post .post-heading .meta .title a {
14        color: black;
15      }
16      .post .post-image .image {
17        width: 100%;
18        height: auto;
19      }
20      .comment-box {
21        padding: 20px;
22        font-size: 18px;
23      }
24      .sample-post {
25        background: #f5f5f5;
26        padding: 20px;
27      }
28      .footer{
29        margin-top: 20px;
30      }
31      .msg_container {
32        border: 1px solid #f5f5f5;
33        margin: 4px 0;
34      }

Updating the index page

Open the ./app/index.html file and include the CDN file for Bootstrap to enhance the styling and layout of our application. Open up the file and add update as shown below:

1<!-- ./app/index.html -->
2     
3    <!DOCTYPE html>
4      <html>
5        <head>
6          <meta charset="utf-8">
7          <meta http-equiv="X-UA-Compatible" content="IE=edge">
8          <title>LiveCommentsEmber</title>
9          <meta name="description" content="">
10          <meta name="viewport" content="width=device-width, initial-scale=1">
11          {{content-for "head"}}
12          <link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
13          <link integrity="" rel="stylesheet" href="{{rootURL}}assets/live-comments-ember.css">
14          <!-- add the CDN file -->
15          <link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
16          {{content-for "head-footer"}}
17        </head>
18        <body>
19          {{content-for "body"}}
20          <script src="{{rootURL}}assets/vendor.js"></script>
21          <script src="{{rootURL}}assets/live-comments-ember.js"></script>
22          {{content-for "body-footer"}}
23        </body>
24      </html>

Testing the application

Stop the Ember development server if it is currently running by typing Ctrl + C in the terminal and restart it with ember serve. In addition, don’t forget to also run node app.js from another terminal to start the Express server incase you haven’t done that. Next, open http://localhost:4200 to test the application:

ember-comments-screenshot

Conclusion

The importance of realtime engagement of users on any platform cannot be overemphasized. As seen in this tutorial, we were able to implement a realtime commenting system and also show emojis based on the mood of the users by using sentiment analysis. I hope you found this tutorial helpful.

Feel free to explore the code for this tutorial on GitHub and add more features as you deem fit.