Want to build a chat app with Angular

Want to build a chat app with Angular

You have to prepare basic Angular knowledge to build

What we are going to be building

Using our application, users will be able to view and send messages on a private channel.

To achieve this, we are going to be using Pusher's API for sending and receiving messages in realtime. So make sure you have your Pusher account at hand, or sign up for free here. We will also write a tiny server-side Node app that will be used by Pusher to authenticate channel subscriptions received from our Angular front end. Let's look at how these three parts (Pusher, Server, Client) fit together.

Project structure

We are going to keep our server-side and client-side apps separate in their respective directories.

chat-app/
  chat-app-client/
  chat-app-server/
Building a Node app

First, let's get the server app out of the way by creating its directory and initializing npm.

$ mkdir chat-app-server
$ cd chat-app-server
$ npm init

This will create a package.json file for our app. Now let's install our dependencies.

$ npm i --save body-parser express pusher

Note that we are installing the pusher Node library as a dependency.

It's also a good practice to define a npm start script. In our package.json file, let's replace the default scripts property with the following.

// package.json
"scripts": {
  "start": "node server.js"
}

This maps the command node server.js to an npm start command. However, server.js doesn't exist as a file yet, so let's create it next.

// server.js
const express = require('express');
const http = require('http');
const bodyParser = require('body-parser');
const api = require('./api.js');
const port = process.env.PORT || '3000';
const app = express();

// define middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
next();
});
app.use('/', api);
app.set('port', port);

const server = http.createServer(app);
server.listen(port, () => console.log(Running on port ${port}));

The calls to our endpoint will be coming in from a different origin, therefore we need to make sure we include the CORS headers (Access-Control-Allow-Origin). If you are unfamiliar with the concept of CORS headers, you can find more information here.

This all is standard Node application configuration, nothing specific to our app.

API Routes

In our server.js file we are referencing a non-existent file api.js on line 5. That's the file where we will define our API routes and the logic behind them.

// api.js
const express = require('express');
const router = express.Router();
const Pusher = require('pusher');

router.get('/', (req, res) => {
res.send('all good');
});

module.exports = router;

We've defined one route for our app: /, that returns an all goodresponse. Now, let's take a step back and define what we want our API to do based on our requirements.

Pusher Channel Authentication

Considering that we will be triggering Pusher events directly from our client, we need to implement an authentication endpoint. This endpoint will be called by Pusher directly in order to authenticate any subscription it receives from our front end.

Pusher will be making a POST request to an endpoint that we will provide. Therefore, let's create a POST /pusher/auth endpoint.

// api.js
router.post('/pusher/auth', (req, res) => {
console.log('POST to /pusher/auth');
});

Whenever Pusher calls the authentication endpoint, it sends a socketid and channelname that we will use to authenticate the incoming subscription.

// api.js
router.post('/pusher/auth', (req, res) => {
console.log('POST to /pusher/auth');
const socketId = req.body.socket_id;
const channel = req.body.channel_name;
});

Next step is to authenticate the subscription using the extracted socketId and channel values from the request. Since we will be telling Pusher to authenticate the subscription, we need to initialize Pusher by instantiating it with our Pusher account credentials first. Let's do that at the top of our api.js file.

// api.js
const pusher = new Pusher({
appId: 'xxxxxx',
key: 'xxxxxxxxxxxxxxxxxxxx',
secret: 'xxxxxxxxxxxxxxxxx',
});

You can find these values by navigating to a specific Pusher application from your personal Pusher dashboard.

We now have an instance of Pusher available to us that we can use for authenticating subscriptions.

// api.js
router.post('/pusher/auth', (req, res) => {
const socketId = req.body.socket_id;
const channel = req.body.channel_name;
const auth = pusher.authenticate(socketId, channel);
res.send(auth);
});

That is all the logic we need to write for our Node app. All that is left now is to run it so that it's ready to start serving requests. We start our app by running npm start.

Building an Angular web app

As you remember, the client app's responsibility is to send and receive new messages from Pusher in real time. Let's get going.

Angular app initialization

We are going to use Angular CLI to quickly scaffold our application. You can follow the installation instructions if you don't have Angular CLI installed already.

After you've installed Angular CLI, it's time to initialize our Angular application. Navigate to the chat-app directory and run the following command to scaffold an application.

$ ng new chat-app-client --skip-tests --routing

After the installation is finished we need to install Pusher's client library.

$ npm i --save pusher-js

Installing the library isn't enough, as we also want to include it on our page. In order to do that we need to add the library to third party scripts to be loaded by Angular CLI. All CLI config is stored in .angular-cli.json file. Modify the scripts property to include the link to pusher.min.js.

// .angular-cli.json "scripts": ["../node_modules/pusher-js/dist/web/pusher.min.js"]

We are now ready to write some more code. However, let's take a step back and figure out the application structure.

Angular application structure

Nothing is stopping us from writing the whole app inside the AppComponent, however, if we want to follow best practices (and we do), then we need to avoid that. AppComponent is best left for any startup initialization we need to do for our app.

Considering that our app is basic, I can only see one other component we can create - MessagesComponent that will be in charge of displaying existing messages, as well as collecting user input and sending new messages.

There will also be one service that we'll need to create - a PusherService that deals with Pusher.

With CLI, we can easily generate components and services. In our instance, we want to run the following commands.

$ ng g c messages $ ng g s pusher


Note: g is an alias for a generate command, c is an alias for component and s is an alias for a service


We now have all our components in place. Even though our service has been created, it hasn't been provided. Therefore we need to manually add it as a provider to the correct application module. However, because we only have one module, AppModule, we can only provide it in there.

// app.module.ts
import { PusherService } from './pusher.service';
...
@NgModule({
providers: [PusherService],
...
})

Pusher initialization

First, let's initialize Pusher. When we were initializing Pusher on the server, we needed three properties: appIdkey and secret, however, we only need the key on the client.

In a real world application, you are likely to use different Pusher keys depending on the environment you are in (like development or production), therefore it is a good idea to store our Pusher key as a property on the environment constant.

Angular CLI creates an environment.ts file that is used to store environment-specific variables. Let's store our Pusher key there.

// environment.ts
export const environment = {
pusher: {
key: '<YOUR_PUSHER_KEY>',
}
};

Now we can use the environment variable in our PusherService when initializing an instance of Pusher. Upon initialization, we will store it in a property on the service that any other component can use.

// pusher.service.ts
import { environment } from '../environment/environment';
...
export class PusherService {
pusher: any;

constructor() {
// replace xxxx's with your Pusher application key
this.pusher = new Pusher(environment.pusher.key);
}
}

At this point, Typescript will complain about our new Pusher(..) expression, because we haven't imported Pusher. However, we don't need to import it, as it exists in the Window object in the browser because we're including the pusher-js library in our index.html. Therefore, to silence the Typescript compiler, we need to declare Pusher at the top of the file along with other imports.

// pusher.service.ts
declare const Pusher: any;
...

In addition to the application key, we also need to instantiate Pusher with our authentication endpoint that we created with Node.

constructor() {
// replace xxxx's with your Pusher application key
this.pusher = new Pusher('xxxxxxxxxxxx', {
authEndpoint: 'http://localhost:3000/pusher/auth',
});
}
localhost:3000 is where our Node application is running.

Now we can have access to our pusher instance through the PusherService class.

Pusher works by publishing events to a specific channel. Whoever is subscribed to that channel will receive the published event. Events can have data associated with them. Our client will be receiving and triggering events to a predefined channel. On top of storing the Pusher instance as a property on the PusherService, let's also store the channel that we will be receiving and triggering messages on.

// pusher.service.ts
...
export class PusherService {
...
messagesChannel: any;

constructor() {
...
this.messagesChannel = this.pusher.subscribe('private-messages');
}
}

It is at this point, during the subscription to a channel that Pusher sends an authentication request to our specified endpoint.

In the snippet above, private-messages is the name of our channel. Triggering events on the front end only works with private or presence channels, therefore we have to prefix our channel's name with private.

MessagesComponent

Our Angular project was initialized with routing because we passed a --routing flag with the initialization command. However, we need to tell Angular to use our MessagesComponent for the default route. Routing is configured in app-routing.module.ts.

// app-routing.module.ts
import { MessagesComponent } from './messages/messages.component';
...

const routes: Routes = [
{
path: '',
component: MessagesComponent,
}
];
...

Now that MessagesComponent loads for our default route, let's work on it.

One of this component's responsibilities is to display existing messages. We could achieve that with an array of messages that we are looping through in the view and display them.

// messages.component.ts
...
interface Message {
text: string;
user: string;
}
export class MessagesComponent {
messages: Array<Message>;
constructor() {
this.messages = [];
}
}
Note: one of the most powerful Typescript features is (obviously) types. They are there to help you, so make sure you use them. Like in the snippet above, I created an interface for an individual message object. As a result, I can specify that messages property is an array of Message and if I try putting anything else in it - Typescript will not allow me to.

We can now loop over the messages array to display individual messages.

// messages.component.html
<div *ngFor="let message of messages">
<b>{{ message.user }}</b>: {{ message.text }}
</div>

Because the messages property is initialized to an empty array, nothing will be displayed. Now that we have a way of storing all messages, let's write the logic to populate our storage.

Realtime messages from Pusher

New messages are going to be coming in via Pusher. We have the channel that will be transmitting messages and it's stored in the PusherService. So what we want to do is start listening for events on that channel and handle whenever an event is received.

Because we want to start listening for events on the channel as soon as the MessagesComponent is initialized, we need to use Angular component lifecycle hooks. Specifically, we are interested in the OnInit lifecycle hook, as it is triggered as soon as the component initialization is finished.

Before we use it, we need to let Angular know that we want to be using this hook by specifying that our component implements the OnInit interface.

// messages.component.ts
import { OnInit } from '@angular/core';
...
export class MessagesComponent implements OnInit {
...

ngOnInit() {}
}

ngOnInit is the method that will be run when the OnInit lifecycle hook is triggered by Angular. That makes it a perfect place to run any sort of initialization logic, in this case, start listening for events. For that, we need to get access to the messagesChannel property on the PusherService

Before we can use PusherService inside our component, we need to inject it as a dependency of this component.

// messages.component.ts
import { PusherService } from '../pusher.service';

export class MessagesComponent {
...
constructor(private pusherService: PusherService) {...}
}

Now we can start listening for events on the messagesChannel property of PusherService.

// messages.component.ts
...
export class MessagesComponent {
...
ngOnInit() {
this.pusherService.messagesChannel.bind('client-new-message', (message) => {
this.messages.push(message);
});
}
}

In the snippet above, we are listening for client-new-message events on the messages channel. The second parameter of the bind is the callback function. It is a function that will be called whenever an event is received with the event data. In our case, the data associated with the event will be the message, therefore we are pushing the new message into our array of messages.

Triggering Pusher events

In the MessagesComponent we need to collect the user's username and message text in order to send it as a new event to Pusher. First, let's add two input fields in our HTML.

// messages.component.html
...
<label for="userName">Username</label>
<input type="text" id="userName" [(ngModel)]="userName">

<label for="message">Message</label>
<input type="text" id="message" [(ngModel)]="messageText">

<button (click)="sendMessage(userName, messageText)">Send</button>

Two things to note here:

  • We are using Angular's two-way binding with ngModel to record user input.
  • We have added a click handler on the button that calls a method sendMessage on our component (this method doesn't exist yet, we will write it soon) with the userName and messageText values.

Because we will be are triggering events directly from the client, we need to prefix the event name with client.

There are also other restrictions that apply when triggering events from the client. Even though I will be pointing them out as we go in this article, I suggest you read the Pusher documentation on the topic to avoid confusion.

Before we can start triggering events from the client, we need to tell Pusher that we intend to do so. You can do that by navigating to your application's settings on your Pusher dashboard.

Inside our sendMessage method, we will want to trigger an event named client-new-message with the message assembled from this method's arguments. On top of that, we need to add this message to our array of messages.

// message.component.ts
...
export class MessagesComponent implements OnInit {
...

sendMessage(user: string, text: string) {
const message: Message = {
user: user,
text: text,
}
this.pusherService.messagesChannel.trigger('client-new-message', message);
this.messages.push(message);
}
}

Finally, we can send messages and display realtime messages!

You might wonder why are we adding the message that we just triggered an event with to our messages array. Surely, it should come back to us as in our event listener that we've defined in the OnInit lifecycle hook of this component?

Well, not exactly. What our event listener does is listen for events on the channel that originated somewhere else, that is a very important aspect. An event from the same origin will not be received on the channel subscription in the same origin.

How can we see our application in all its glory? First, we need to make sure our server is running. Remember, to run it we simply need to run npm start from our chat-app-server directory. Second, we need to run our front-end application, which we can easily do with Angular CLI, by running ng servefrom its root directory. By default, the application will be run on localhost:4200.

Now in order to actually test that our chat application is fully operational, we can open two browser tabs/windows side by side with our Angular application running. Messages sent from one window will appear in another window. Voila, our chat app is fully functional! ?

Congratulations, you have built a chat app. Thank for reading !

Originally published on https://pusher.com


Single Page Application with Angular.js Node.js and CouchDB

Single Page Application with Angular.js Node.js and CouchDB

We posted the article on Single Page Application with Angular.js, Node.js and CouchDB with Express

We posted the article on Single Page Application with Angular.js, Node.js and CouchDB Cradle Module with Express 3. Current post is a way to migrate the same with Express 4.

This article describes a way to make a web application with Javascript based Web Server with CouchDB with Angular.js and Node.js.

Here we have selected

  • Angular.js for client side development – Single Page Application
  • Cross Domain Communication in between Angular.js and Node.js
  • Node.js for server side development
  • Rest based web service creation with express.js (Express 4)
  • Database – CouchDb
  • Node.js Cradle Module Extention (to make communication with CouchDB)
  • Making CouchDB views with Map-Reduce to fetch the data

We have created a Proof of Concept with Javascript based web server, where we have focused on dealing with NoSql (CouchDB) with javascript based framework Node.js and angular.js on client side.

Architecture at a glance



Steps

Installation

  • Download and install Node.js from here.
  • To Develop the application we need to install cradle module for Node.js
  • Command – **npm install cradle **(should be connected to internet)
  • We need to install express.js for node.jsCommand – npm install express (should be connected to internet)

Configuration Code

Now we will try to describe the code portion

var application_root = __dirname,
express = require("express"),
path = require("path");

Here we have initialised the express.js within javascript variables in respect of Node.js concept.

var databaseUrl = "sampledb";
var cradle = require('cradle');
var connection = new(cradle.Connection)('http://admin:[email protected]', 5984, {
auth: { username: 'admin', password: 'admin' }
});
var db = connection.database(databaseUrl);

Here we have initialised the express web server in app variable.

var databaseUrl = "sampledb";
var cradle = require('cradle');
var connection = new(cradle.Connection)('http://admin:[email protected]', 5984, {
auth: { username: 'admin', password: 'admin' }
});
var db = connection.database(databaseUrl);

Here we have made the connection to the couchdb database using the Node.js cradle module extension library.

// Config
var env = process.env.NODE_ENV || 'development';
if ('development' == env) {
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
var methodOverride = require('method-override');
app.use(methodOverride());
app.use(express.static(path.join(application_root, "public")));
var errorhandler = require('errorhandler');
app.use(errorhandler());
}

Here we have made the configuration related to express.js which is very much different from Express 3.

In Express 3 the same portion was written as

      app.configure(function () {
         app.use(express.bodyParser());
         app.use(express.methodOverride());
         app.use(app.router);
         app.use(express.static(path.join(application_root, "public")));
         app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
      });

This makes a little sense how the Express 3 is migrated to Express 4.

In Express 4 app.use(app.router); is removed. Instead it is added with the Rest services.The way of configuration is also changed as well as the changes reflected in use of bodyParser() ,methodOverride() ,methodOverride() ,errorHandler({…}).

Rest Services Code

app.route('/api').get(function (req, res) {
   res.header("Access-Control-Allow-Origin", "http://localhost");
   res.send('Express API is running');
});

Here we have made our first REST based web service and tested whether the express.js is running.Our sample api will be http://127.0.0.1:1212/api or http://localhost:1212/api

app.route('/getangularusers').get(function (req, res) {
 res.header("Access-Control-Allow-Origin", "http://localhost");
 res.header("Access-Control-Allow-Methods", "GET, POST");
 res.writeHead(200, {'Content-Type': 'application/json'});
 str='[';
 db.view('characters/all', function (err, response) {
    response.forEach(function (row) {
            //console.log("%s is on the %s side of the force.", row.name, row.force);
        str = str + '{ "name" : "' + row.username + '"},' +'\n';
        });
        str = str.trim();
        str = str.substring(0,str.length-1);
    str = str + ']';
    res.end( str);
 });
});

Here we have created another REST api to get all username from user collection and so we have done the cradle query.


CouchDB View Creation

This view creation is to be executed, before running the Angular Client Application to get specific set of data from couchdb.

To run this view creation, we should put

[httpd]  
authentication_handlers = {couch_httpd_auth, null_authentication_handler}  

in the local.ini file of <>/etc/couchdb folder to execute this map function without being the admin user.

So the REST api of view creation is

app.route('/createview').get(function (req, res) { // Before running the Angular Application, the view must be created
 db.save('_design/characters', { // Main View Family Name
        all: { // A particular set of data selection by javascript map-reduce
        map: function (doc) {
           if (doc.username) emit(doc.username, doc); // specific code to execute map function on each document
       }
     }
   });
});

and the sample api will be – http://127.0.0.1:1212/getangularusers (Get Method).

Next we have made a POST request to create an user via REST calling.

app.route('/insertangularcouchuser').post(function (req, res){
console.log("POST: ");
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header("Access-Control-Allow-Methods", "GET, POST");
// The above 2 lines are required for Cross Domain Communication(Allowing the methods that come as Cross
// Domain Request
console.log(req.body);
console.log(req.body.mydata);
var jsonData = JSON.parse(req.body.mydata);
var doc = {email: jsonData.email, password: jsonData.password, username: jsonData.username};
db.save('\''+Math.random()+'\'', doc, function (err, res) {
   if (err) {
       // Handle error
       res += ' SAVE ERROR: Could not save record!!\n';
       } else {
       // Handle success
       res += ' SUCESSFUL SAVE\n';
       }
   });
});

Our sample api will be – http://127.0.0.1:1212/insertangularcouchuser (Post Method – accessed by client side)

// Launch server
app.listen(1212);

We have made the server to listen at 1212 port.

Now run node appcouchdbangular.js from command shell. This is our node.js specific code file.

For references :


Angular.js part

Below is the code in Angular Controller

'use strict';

var myApp = angular.module('myApp', []); // <strong>Taking Angular Application in Javascript Variable</strong>

// <strong>Below is the code to allow cross domain request from web server through angular.js</strong>
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);

/* Controllers */

function UserListCtrl($scope, $http, $templateCache) {

var method = 'POST';
var inserturl = 'http://localhost:1212/insertangularcouchuser';// <strong>URL where the Node.js server is running</strong>
$scope.codeStatus = "";
$scope.save = function() {
//<strong> Preparing the Json Data from the Angular Model to send in the Server.</strong>
var formData = {
'username' : this.username,
'password' : this.password,
'email' : this.email
};

this.username = '';
this.password = '';
this.email = '';

var jdata = 'mydata='+JSON.stringify(formData); // &lt;strong&gt;The data is to be string.&lt;/strong&gt;

$http({ &lt;strong&gt;// Accessing the Angular $http Service to send data via REST Communication to Node Server.&lt;/strong&gt;
        method: method,
        url: inserturl,
        data:  jdata ,
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        cache: $templateCache
    }).
    success(function(response) {
    console.log("success"); &lt;strong&gt;// Getting Success Response in Callback&lt;/strong&gt;
            $scope.codeStatus = response.data;
    console.log($scope.codeStatus);

    }).
    error(function(response) {
    console.log("error"); &lt;strong&gt;// Getting Error Response in Callback&lt;/strong&gt;
            $scope.codeStatus = response || "Request failed";
    console.log($scope.codeStatus);
    });
$scope.list();&lt;strong&gt;// Calling the list function in Angular Controller to show all current data in HTML&lt;/strong&gt;
    return false;

};

$scope.list = function() {
var url = 'http://localhost:1212/getangularusers';// <strong>URL where the Node.js server is running</strong>
$http.get(url).success(function(data) {
$scope.users = data;
});
<strong>// Accessing the Angular $http Service to get data via REST Communication from Node Server </strong>
};

$scope.list();
}

Angular Template and HTML

       <html lang="en" ng-app="myApp">
.....

We have referred the Angular Application in above code

   <body ng-controller="UserListCtrl">
.....

We have referred the Angular Controller in above code

   Search: <input ng-model="user">
<div class="span10">
<!--Body content-->
<ul class="users">
<li ng-repeat="user in users | filter:user ">
{{user.name}}
</li>
</ul>
</div>

We have used the ng-repeat tag to take the users data model from REST communication and shown in HTML

   <form name="myform" id="myform1" ng-submit="save()">
<fieldset>
<legend>New User</legend>
<div class="control-group">
<center>
<input type="text" placeholder="User…" ng-model="username" size=50 required/>
</center>
<center>
<input type="text" placeholder="Password…" ng-model="password" size=50 required/>
</center>
<center>
<input type="text" placeholder="Email…" ng-model="email" size=50 required/>
</center>
</div>
</fieldset>
<p>
<div><center><button type="submit" >Save now...</button></center></div>
</p>
</form>

We have used the ng-submit tag to send the user data model from REST communication and sent to node server to save in CouchDB.

Reader can download the complete source-code in GitHub.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Learn More

The Complete Node.js Developer Course (3rd Edition)

Angular & NodeJS - The MEAN Stack Guide

NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

Node.js: The Complete Guide to Build RESTful APIs (2018)

Angular 7 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Web Developer Bootcamp

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

Angular 7 CRUD With Node.JS API

Angular and Nodejs Integration Tutorial

Angular + WebSocket + Node.js Express = RxJS WebSocketSubject ❤️

Setting up your new Mac for MEAN Stack development

Front-end Developer Handbook 2019

Originally published on https://codequs.com

Hetu Rajgor's answer to What are the major differences between Java, AngularJS, and JavaScript and Node JS? - Quora

<img src="https://moriohcdn.b-cdn.net/70b437cf37.png">Java is a programming language which is owned by Oracle. More than 3 Million devices are running in Java.&nbsp;JS is a client-side programming language used for creating dynamic websites and apps to run in the client's browser.

Java is a programming language which is owned by Oracle. More than 3 Million devices are running in Java. JS is a client-side programming language used for creating dynamic websites and apps to run in the client's browser.

Node vs Angular : Comparing Two Strong JavaScript Technologies

Just from being a simple client-side scripting language, JavaScript has evolved over the years to turn out to be a powerful programming language. Here Node.js is a cross-platform runtime environment while AngularJS is one of the top JavaScript framework. Angular helps the developers to build web applications which are dynamic in nature using HTML template language and following the MVC design pattern.