Nowadays real-time functionality is a mandatory functionality for web applications. Most of the customers want to see how users interact with their applications in real-time. So I thought to develop a sample application to look at the working of real-time application. In this tutorial, we are going to work together to build a real-time application with Angular 8, Node and WebSockets.
Here we are going to develop a notification application to notify the admin of the comments of the page. In simple words, the admin will get a notification when the user enters a comment.
What is Real-time Application?
A real-time application allows information to be received on the user-end soon as soon as it is published, User is directly notified about the new update. It makes it easier for the user to receive the updates directly, instead of putting an effort to search for what he needs. Therefore, this kind of application will increase interactivity between the user and the application.
What is WebSocket?
WebSocket is a protocol for creating a fast two-way channel between a web browser and a server. WebSocket overcomes limitations with HTTP to allow low latency communications between a user and a web service.
Project Setup
WebSockets widely supported by more platforms but for this sample, I’m using Angular 8 for client APP and Node.js for the server. Why Angular? It is because Angular is the most powerful front-end framework and we have got huge support. You can also use whichever front-end and server framework if you are able to find necessary plugins for WebSockets. We will be currently working on this environment,
- Node.js 10.16.3
- Angular 8.3.19
- Npm 6.9.0
Let’s dig into the code now.
Server Code
Since we are going to use WebSocket you can find some WebSocket Sample here. To create a WebSocket environment you need to run the below comments
mkdir socket-server cd socket-server mkdir src npm init npm i express socket.io @types/socket.io --save
npm init will fire you some questions like below,
package name: (socket-server) version: (1.0.0) description: Sample Socket Server entry point: (index.js) : test command: git repository: keywords: author: license: (ISC)
After answering all the above questions system will generate a package.json file inside your application folder.
{ "name": "socker-server", "version": "1.0.0", "description": "Sample Server", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
npm i express socket.io @types/socket.io –save will install the dependencies we need for run WebSocket. Now the environments ready to start server code.
Now create an app.js inside the src folder to hold our server code.
At the top, we need to require the dependencies plugins.
const app = require('express')(); const http = require('http').Server(app); const io = require('socket.io')(http);
require statements will tell the system we’re going to use Express and Socket.IO. Socket.IO is coming with some nice features like fallback mechanism, It had the ability to create “rooms” for older browsers that do not support WebSockets and we can see the response in minutes.
In our next line, we are going to allocate memory to store our feedback.
const comments = {};
Now let’s define what we want our socket server to do,
io.on("connection", socket => { let previousId; const safeJoin = currentId => { socket.leave(previousId); socket.join(currentId); previousId = currentId; }; socket.on("addComment", comment => { comments[comment.id] = comment; safeJoin(comment.id); io.emit("comments", comments); socket.emit("comment", comment); }); socket.on("typingComment", flag => { io.emit("typing", flag); }); io.emit("comments", comments); });
Let’s dig deep, .on(‘…’) is an event listener in which we can use predefined events or we can create our own events. For, on the first parameter is events name and second is a callback function. we can tell what needs to perform when the event fires.
The first line .on(‘connection’) is used to find the use connection when the user connects. We will get the socket variable. With the socket variable, we can initiate communication with single or multiple sockets. (connection is predefined event type available in Socket.IO)
Here we have two user-defined event types that our socket is listening for from the client.
- addComment
- typingComment
And, two event types that are emitted by our socket to the client.
- comments
- typing
When the client emits the addComment event with comment object, we will push the new comment to comments object and we want to announce everyone who is connected to our server that new comment added to the page. So we are broadcasting the message to all clients with io.emit(‘comments’) so all connected clients will get updated immediately.
And the io.emit(‘typing’) let know the client that somebody started typing.
After the socket functions are all set up, pick a port and listen to it.
http.listen(4444);
We now have a fully-functioning socket server for document collaboration! Run $ node src/app.js to start it.
Angular Client APP
Now the server is ready to communicate. Let’s start to build the client.
ng new socket-app --routing=false --style=SCSS cd socket-app npm i ngx-socket-io --save ## This is an Angular wrapper over socket.io client libraries ng g class models/comments ng g c components/comment-list ng g s services/comment
Add depending modules in app.module.ts in before @NgModule declaration, add these lines of code to it.
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; const config: SocketIoConfig = { url: 'http://localhost:4444', options: {} };
Now add it to your imports array, so it looks like:
imports: [ BrowserModule, FormsModule, ReactiveFormsModule, SocketIoModule.forRoot(config) ],
SocketIoModule.forRoot(config) this will fire off the connection to our socket server as soon as AppModule loads.
Comment service
To handle the socket connection and handle comments event.
models/comments.js
export class Comment { id: string; name: string; comment: string; }
Add the following definition to services/comment.service.ts file.
import { Injectable } from '@angular/core'; import { Socket } from 'ngx-socket-io'; import { Comment } from '../models/comments'; @Injectable({ providedIn: 'root' }) export class CommentService { currentComment = this.socket.fromEvent<Comment>('comment'); comments = this.socket.fromEvent<any>('comments'); typing = this.socket.fromEvent<any>('typing'); constructor(private socket: Socket) { } newComment(comment) { const date = (new Date()); const value = { id: this.commentId(), name: comment.name, comment: comment.comment }; this.socket.emit('addComment', value); } typingComment(flag) { this.socket.emit('typingComment', flag); } private commentId() { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 5; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } }
The methods here represent each emits. The two event types that the socket server is listening for, and the properties typing and comments represent the events emitted by the socket server, which is consumed on the client as an Observable, so we can do a lot of cool things with them if we wanted.
You may notice a call to this.commentId(). This is a little private method I created in this service that generates a random string to assign as the comment id.
Let’s put the list of comments under the page content to display the comments in the client and create a form to get the comment from the client. In document-list.component.html, write the following
<div class="comment-block"> <p> Comment your thought </p> <div> <ul class="comment-li"> <li *ngFor='let comment of comments | async | keyvalue'> <p> Name: {{comment.value.name}} <br> {{comment.value.comment}} </p> </li> </ul> </div> <div *ngIf='typing | async'>Someone Typing...</div> <div> <form [formGroup]="commentForm" (ngSubmit)="onSubmit(commentForm)"> <div class="form-row"> <div class="form-group col"> <label> Name: </label> <input type="text" class="form-control" formControlName="name"> </div> </div> <div class="form-row"> <div class="form-group col"> <label> Comment: </label> <textarea [(ngModel)]="description" (keyup)='typingComments(true)' (blur)='typingComments(false)' class="form-control" formControlName="comment"> </textarea> </div> </div> <div class="text-center"> <button class="btn btn-primary mr-1" type="submit">Submit</button> </div> </form> </div> </div>
In comment-list.component.ts, add the following in the class definition,
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { CommentService } from 'src/app/services/comment.service'; import { Comment } from 'src/app/models/comments'; import { Observable } from 'rxjs'; @Component({ selector: 'app-comment-list', templateUrl: './comment-list.component.html', styleUrls: ['./comment-list.component.scss'] }) export class CommentListComponent implements OnInit { commentForm: FormGroup; comments: Observable<any>; typing: Observable<any>; description = ''; constructor( private formBuilder: FormBuilder, private commentService: CommentService ) { } ngOnInit() { this.comments = this.commentService.comments; this.typing = this.commentService.typing; this.commentForm = this.formBuilder.group({ name: ['', Validators.required], comment: ['', Validators.required] }); } onSubmit(formGroup) { if (formGroup.valid) { this.commentService.newComment(formGroup.value); this.description = ''; } } typingComments(flag) { this.commentService.typingComment(flag); } }
Let’s start with the properties. comments will be a stream of all available comments emit by a socket. typing is a user socket variable that will get the typing status of the user. onSubmit function will get the values from the input and using the comment.service function newComment will push data to addComment event. comments and typing will act as an Observable and will emit and change the UI once data changes.
Now to loading a comment-list on the home page just replace the app.component with below.
<app-comment-list></app-comment-list>
We are done with our coding part let start run our front-end to see the output.
ng serve
Open more than one instance of http://localhost:4200 (I’ve done it here in separate browsers for added wow factor) and watch it in action.
Find the full source code HERE.
Share your views and doubts in the comment section below.
Like this tutorial? Take a look at our blog for more such interesting things you shouldn’t miss. Subscribe for the exclusive newsletters from Agira.
Are you looking for a perfect Angular developer for your app project? Agira Technologies is a Leading Web and mobile development company. Hire an Angular developer now!