Build a Real-Time App with Deepstream and Angular

March 12, 2018
by Rahil Shaikh
  • Build a realtime app with Angular and deepstream

Real-time technologies are gaining momentum, applications that require syncing data not just between multiple clients but also with backend services and databases are on a rise. A popular example would be a trading application, in which response time and real-time syncing are of great importance. Deepstream aims to solve this interesting problem with ease.

In this tutorial, we will see how we can build a real-time app with Deepstream. We will build a real-time timeline application where one user posts an update it would immediately show up to another user connected to the client. We will use Angular 4 to build our front-end.

Setup Deepstream Server

Deepstream can be installed for our OS or can be set up using docker or npm. For our tutorial, we will set it up using npm.

In your project directory create a file named server.js. Install deepstream.io.

npm install deepstream.io

Add the following lines to server.js.

server.js
const Deepstream = require('deepstream.io');

const server = new Deepstream();

server.start();

That’s it three lines. Now we can start our Deepstream server by running node server.js.

Setup Angular

I’ll be using angular-CLI to scaffold our application. Angular CLI will make our development easy. We will not go into too many details of setting up our Angular app as that’s not our focal point.

To begin with, we will just have a simple app which displays a timeline and allows the user to add to the timeline. For the demo we won’t have user management, we will just let user type in the username.

This our app looks like.

Deepstream and Angular

The user would come in and enter the username and start posting on the timeline. We are just pushing the contents that the user posts and iterating it using the ngFor directive to construct our timeline.  You can have a look at the code we have got so far. I have also used [email protected] for a bit of styling.

app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
  userStatus = "";
  user = "";
  timeline=[];

  /** sets user's status */
  onKey(ev){
    this.userStatus = ev.target.value;
  }

  /** add a new post to timeline */
  addPost(){
    if(!this.user){
      return alert("Enter your name, to proceed.");
    }
    this.timeline.unshift({
      author: this.user,
      content: this.userStatus
    });
    this.userStatus = "";
  }
 
  /** Sets user's name */
  setUser(ev){
    this.user = ev.target.value;
  }
}
app.component.html
<div class="container">
  <div class="row">
    <div style="margin-top:20px;" class="col-md-3 col-md-offset-3">
      <div class="form-group">
        <h4>Enter your name</h4>
        <input type="text" class="form-control" (change)="setUser($event)" placeholder="Enter your name"/>
      </div>
    </div>
    <div class="col-md-6 col-md-offset-3">
      <h3>Post your ideas!</h3>
      <div class="col-md-12">
        <textarea  (keyup)="onKey($event)" [value]="userStatus" rows="2" style="width:100%" placeholder="What's on your mind?"></textarea>
        <button (click)="addPost()" class="btn btn-primary pull-right">Post</button>
      </div>
      <h3>Legendary Timeline</h3>
      <div style="margin-top:10px;" class="col-md-12 timeline-element" *ngFor="let elem of timeline">
        <div class="col-md-2 thumb">
            <img src="assets/user.jpg" alt="..." class="img-thumbnail">
        </div>
        <div class="col-md-10">
            <h4>{{elem.author}} <small>says...</small></h4>
            <p>{{elem.content}}</p>
        </div>
      </div>
    </div>
  </div>
</div>
style.css
/* You can add global styles to this file, and also import other style files */
.timeline-element {
    background-color: #eeeeee;
    border-top: 1px solid #ccc;
}
.thumb {
    margin-top: 5px;
}

Now, this is very basic. Nothing interesting, It’ time to make our app real-time. Basically what we want to do is, whenever a user enters something into the timeline we wanna make sure it is displayed instantaneously to other users. Deepstream will help us do that.

Going Real-time with Deepstream.io

We already have our Deepstream server running. Now we will integrate Deepstream client into our angular app.

Deepstream provides a JavaScript client SDK which we can use. Let’s first install it. Navigate to your Angular app folder and run…

npm i deepstream.io-client-js --save

Deepstream,Angular, Realtime App
Include it under scripts in our angular-cli.json

...
..
"scripts": [
        "../node_modules/deepstream.io-client-js/dist/deepstream.js"
      ],
..
...

We can directly now use the Deepstream client in our component, but it’s better to wrap it into a service.

Create a file named app.service.ts.

app.service.ts
import { Injectable } from "@angular/core";

declare var deepstream;

@Injectable()
export class DsService {
    public ds;

    constructor(){
        this.ds = deepstream('localhost:6020');
    }
}

Note, our Deepstream server is running on localhost:6020.

A brief of records and lists in Deepstream

Records are the basic building blocks in Deepstream, they are nothing buy JSON object as data and a bunch of methods that allow us to watch and listen to them for changes. This means whenever a record is changed Deepstream notifies all it’s connected clients about the changes and syncs it across. Deepstream also allows us to add storage/ cache connectors which will persist the record data in the connected database.

Lists are nothing but a collection of records. Lists only store the names of records it holds. Deepstream also allows us to listen to changes on the list as well.

Going Realtime

Now we are going to make some changes to our component to integrate Deepstream into it.

Import the DsService we created and instantiate it in our constructor.

app.component.ts
import { Component, OnInit } from '@angular/core';
import { DsService } from './app.service';
...
...
..
export class AppComponent implements OnInit{
...
..
constructor(private dsservice: DsService){

  }
..
}

Next, we will add some setup when our component Initializes. Check out ngOnInit function.

app.component.ts
  ngOnInit(){
    /** Login to ds */
    this.client = this.dsservice.ds.login();

    const list = this.client.record.getList('timeline'); //This gets the list or creats if it doesn't exists
   
   
    list.whenReady( list =>{
      /** Fetch all existing entries */
      const entries = list.getEntries();

      for(let i =0; i<entries.length; i++){
        this.client.record.getRecord(entries[i]).whenReady(record =>{
          record.subscribe(data => {
            this.timeline.unshift(data);
          }, true);
        });
      }

      /** Listen to new entries */
      list.on('entry-added', (recordName)=>{
        this.client.record.getRecord(recordName).whenReady(record =>{
          record.subscribe(data => {
            this.timeline.unshift(data);
          }, true);
        });
      });
    });
  }

First, we log-in to our DS server. Deepstream allows us to log in anonymously without using credentials which is enough for our demo app.
Next, we get our list timeline. This list will hold all our status records.

Now since our app may restart when we have new updates or any other reason we need to fetch the existing records and construct our timeline. list.getEntries(); gives us the existing entries then we iterate through the list and subscribe to each record. At last we are adding the records in front of out this.timeline array, which is used to display the data on the UI.

list.on('entry-added', (recordName)={..}) allows us to listen to new entries in the timeline list. This helps us update our Timeline in real-time whenever any user updates his status.

Now, we need to create a new record and add it to our timeline list whenever users post something. Change our addPost() method to look like below.

app.component.ts
 /** add a new post to timeline */
  addPost(){
    if(!this.user){
      return alert("Enter your name, to proceed.");
    }
   
    const uid = Math.floor(Math.random() * 1000); //temp way for demo
    const recordName = `status/${uid}`; //create a unique record name
    const record = this.client.record.getRecord(recordName);
    record.whenReady(record => {
      // data has now been loaded
      record.set({
        author: this.user,
        content: this.userStatus
      });
      const list = this.client.record.getList('timeline');
      list.addEntry(recordName); //add to the list
      this.userStatus = "";
    });
  }

Each record needs to have a unique name. For the demo, I’m using a simple Math function, but in real app better method like uuid should be used, Deepstream also provides a method getUid to generate unique ids. Once the record is created we set the data and add it to the List.
Now the entry-added listener on each client will listen to this change and update our timeline.

In case you missed anything here is the complete app.component.ts file.

app.component.ts
import { Component, OnInit } from '@angular/core';
import { DsService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  title = 'app works!';
  userStatus = "";
  user = "";
  timeline=[];
  client;

  constructor(private dsservice: DsService){

  }

  ngOnInit(){
    /** Login to ds */
    this.client = this.dsservice.ds.login();

    const list = this.client.record.getList('timeline'); //This gets the list or creats if it doesn't exists
   
   
    list.whenReady( list =>{
      /** Fetch all existing entries */
      const entries = list.getEntries();

      for(let i =0; i<entries.length; i++){
        this.client.record.getRecord(entries[i]).whenReady(record =>{
          record.subscribe(data => {
            this.timeline.unshift(data);
          }, true); //true invokes callback for the first time as well.
        });
      }

      /** Listen to new entries */
      list.on('entry-added', (recordName)=>{
        this.client.record.getRecord(recordName).whenReady(record =>{
          record.subscribe(data => {
            this.timeline.unshift(data);
          }, true);
        });
      });
    });
  }

  /** sets user's status */
  onKey(ev){
    this.userStatus = ev.target.value;
  }

  /** add a new post to timeline */
  addPost(){
    if(!this.user){
      return alert("Enter your name, to proceed.");
    }
   
    const uid = Math.floor(Math.random() * 1000); //temp way for demo
    const recordName = `status/${uid}`; //create a unique record name
    const record = this.client.record.getRecord(recordName);
    record.whenReady(record => {
      // data has now been loaded
      record.set({
        author: this.user,
        content: this.userStatus
      });
      const list = this.client.record.getList('timeline');
      list.addEntry(recordName); //add to the list
      this.userStatus = "";
    });
  }

  /** Sets user's name */
  setUser(ev){
    this.user = ev.target.value;
  }
}

Seeing our app in action

Start you deepstream server by running..

node server.js

Start you Angular app using ..

ng serve

Now open up your app on two browser windows, use an incognito window. You can also run the same Angular client of different ports. Now, enter the username and start updating your status. You should immediately see the status update from one window reflected onto another. Check out the below video for a demo.

Now even if you refresh the page, your timeline will be constructed, this is because we are fetching the records in the list at the time of initializing the component. However, if you restart your Deepstream server, the data will disappear, this is because Deepstream by default stores data in it’s memory, we can persist the data by connecting any database using the Deepstream storage connectors.

You can grab the source-code from the below link.

Conclusion

In this tutorial, we learned how we can build a Real-time application using Deepstream and Angular. Deepstream is a very powerful tool for real-time syncing of data from our servers to clients, moreover, we can also sync data to a storage engine. This tutorial offered a glimpse of how powerful real-time applications can be, in our further tutorials we will explore other options for building real-time applications.

About

Engineer. Blogger. Thinker. Loves programming and working with emerging tech. We can also talk on Football, Gaming, World Politics, Monetary Systems.

Free PDF

Subscribe and get AngularJS Tips PDF. We never spam!
First Name:
Email:

Leave a Comment