Build a live comments feature with Ember.js
You will need Node and npm installed on your machine.
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:
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
, hencereq.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:
// change directory
cd live-comments-ember
// start the development server
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:
Installing server dependencies
Run the following command to install the dependencies required for this project using:
npm install --save axios pusher pusher-js
npm install --save body-parser cors dotenv express sentiment
ember install ember-browserify
Pusher account setup
Head over to Pusher and sign up for a free account.
Create a new app by selecting Channels apps on the sidebar and clicking Create Channels app button on the bottom of the sidebar:
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:
You can retrieve your keys from the App Keys tab:
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:
PUSHER_APP_ID=YOUR_APP_ID
PUSHER_APP_KEY=YOUR_APP_KEY
PUSHER_APP_SECRET=YOUR_APP_SECRET_KEY
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:
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const Pusher = require('pusher');
const Sentiment = require('sentiment');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_APP_KEY,
secret: process.env.PUSHER_APP_SECRET,
cluster: process.env.PUSHER_APP_CLUSTER,
encrypted: true
});
app.set('port', process.env.PORT || 3000);
app.post('/posts', (req, res) => {
const sentiment = new Sentiment();
const sentimentScore = sentiment.analyze(req.body.text).score;
const payload = {
text: req.body.text,
sentiment: sentimentScore
}
pusher.trigger('chat', 'post', payload);
res.send(payload)
})
app.listen(app.get('port'), () => {
console.log("Listening at " + app.get('port'));
})
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 applicationcomment-post
: this component will display each comment submitted by users in realtimelist-``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:
{{!-- ./app/templates/components/comments-app.hbs --}}
<div class="row comment-box">
<div class="col-md-6 col-md-offset-3">
<div class="sample-post">
<p>Does your team know which product feature really makes a difference
for customers? Monitor industry and customer
trends on a broad scale with our advanced sentiment technology
to develop products that succeed.
</p>
</div>
</div>
</div>
<div class="row comment-box">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-white post panel-shadow">
{{#list-posts}}{{/list-posts}}
</div>
</div>
</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:
{{!-- ./app/templates/components/comment-post.hbs --}}
<div class="post-heading">
<div class="pull-left image">
<img src="http://bootdey.com/img/Content/user_1.jpg" class="img-circle avatar" alt="user profile image">
</div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b> Anonymous </b></a>
posted a comment.
</div>
<h6 class="text-muted time">{{ comment.mood }}</h6>
<p>{{comment.text}}</p>
</div>
</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
:
// ./app/components/list-posts.js
import Component from '@ember/component';
import axios from 'npm:axios';
import Pusher from 'npm:pusher-js';
const SAD_EMOJI = [55357, 56864];
const HAPPY_EMOJI = [55357, 56832];
const NEUTRAL_EMOJI = [55357, 56848];
export default Component.extend({
comments: null,
init() {
this._super(...arguments);
this.set('comments', []);
let pusher = new Pusher('YOUR_APP_KEY', { // update your APP_KEY
cluster: 'CLUSTER',
encrypted: true
});
const channel = pusher.subscribe('chat');
channel.bind('post', data => {
const analysis = data.sentiment > 0 ? HAPPY_EMOJI : (data.sentiment === 0 ? NEUTRAL_EMOJI : SAD_EMOJI);
const response = {
text: data.text,
mood: String.fromCodePoint(...analysis)
}
this.get('comments').pushObject(response);
});
},
actions: {
newPost() {
const text = this.get('newPost');
axios.post('http://localhost:3000/posts', { text });
this.set('newPost', '');
}
}
});
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:
{{!-- ./app/templates/components/list-posts.hbs --}}
{{#each comments as |comment|}}
<div class="row msg_container base_receive">
{{#comment-post comment=comment}}{{/comment-post}}
</div>
{{/each}}
<div class="footer">
<div class="input-group">
{{ input value=newPost id="btn-input" type="text" class="form-control input-sm" placeholder="Write a comment"}}
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" {{action "newPost"}}>Post Comment</button>
</span>
</div>
</div>
Update the app template
Update the application template with:
{{!-- ./app/templates/application.hbs --}}
{{#comments-app}}{{/comments-app}}
{{outlet}}
Stylesheet
To add some minimal styling to the page, open ./app/styles/app.css
and paste this code in it:
// ./app/styles/app.css
.post .post-heading {
height: 95px;
padding: 20px 15px;
}
.post .post-heading .avatar {
width: 60px;
height: 60px;
display: block;
margin-right: 15px;
}
.post .post-heading .meta .title a {
color: black;
}
.post .post-image .image {
width: 100%;
height: auto;
}
.comment-box {
padding: 20px;
font-size: 18px;
}
.sample-post {
background: #f5f5f5;
padding: 20px;
}
.footer{
margin-top: 20px;
}
.msg_container {
border: 1px solid #f5f5f5;
margin: 4px 0;
}
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:
<!-- ./app/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>LiveCommentsEmber</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/live-comments-ember.css">
<!-- add the CDN file -->
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
{{content-for "head-footer"}}
</head>
<body>
{{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/live-comments-ember.js"></script>
{{content-for "body-footer"}}
</body>
</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:
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.
15 June 2018
by Christian Nwamba