Build a Chat Application with Angular Material and the Nexmo In-App JavaScript SDK

In this tutorial, we’ll enable chat in an Angular web application using the JavaScript SDK and the In-App API so that users can communicate in our application. If you’d like to check out the source code, it lives on our community github page.

Setup the CLI to use your Nexmo API Key and API Secret. You can get these from the setting page in the Nexmo Dashboard:

1

$nexmo setup api_key api_secret

The middleware code from Github

Getting the middleware code from Github

First, we’re going to clone the middleware source code and install the dependencies for it. We’re going to write a Node.js application using Express that provides a level of abstraction between the Nexmo API and the Angular code:

1

2

3

4

$git clonegit@github.com:Nexmo/stitch-demo.git

$cd stitch-demo/

$npm install

Running the middleware code from Github

Before we can run the code, we’ll need to create a Nexmo RTC application within the Nexmo platform to use within this code:

The first item is the Application ID, which you should take a note of (we’ll refer to this as APP_ID later). The last value is a private key location. The private key is used to generate JWTs that are used to authenticate your interactions with Nexmo.

Now we’ll need to make a copy of example.env and call that .env, and update the values within your Nexmo API_KEY and API_SECRET. We’ll also have to add the APP_ID we just generated and the path to your private key. After we’ve updated the values, we can run the code in debug mode with:

1

2

$DEBUG=stitch-demo:*node./bin/www

Create Users and Conversations

The app should be running on localhost:3000. Now that the app is running, we’re going to go ahead and create a couple of users and a conversation, and then we’ll add the users we created to the conversation.

We’ll create a couple of users by running this command twice, once with the username alice and then with jamie:

We’ll make a note of the conversation ID and refer to it later on as CONVERSATION_ID. Now let’s join the users to the conversation. We’re going to run the following command twice—remember to replace the CONVERSATION_ID and USER_ID with IDs from the two previous steps every time you run this command:

1

2

3

4

5

6

7

8

$ curl --request PUT \

--url http://localhost:3000/api/conversations \

--header 'content-type: application/json' \

--data '{

"conversationId": "CON-aaaaaaaa-bbbb-cccc-dddd-0123456789ab",

"userId": "USR-aaaaaaaa-bbbb-cccc-dddd-0123456789ab",

"action": "join"

}'

Generate Angular App

Now that we have our middleware up and running, it’s time we created the Angular application. We are going to use the Angular CLI to generate the app, so if you don’t have it installed, you’ll need to install that first:

1

2

$npm install-g@angular/cli

And then we’ll use it to generate a new application with routing. It may take a while for it to generate all the files and install the dependencies:

1

2

$ng newnexmo-stitch-angular--routing

Add Material Design

After the previous command finished, we’ll add Angular Material and its dependencies to the project:

1

2

$npm install--save@angular/material@angular/cdk@angular/animations

We’ll also have to import the NgModule for each component we want to use in our application. In order to do that, we need to open src/app/app.module.ts in our editor and import the modules at the top:

Polyfill Nexmo In-App SDK

Now that we’ve got our Angular application generated and all set up with Material, we’ll install the Nexmo JavaScript SDK and add it to the bottom of polyfills.ts:

1

2

$npm install--save nexmo-conversation

1

2

3

4

5

/**********************************************************

* APPLICATION IMPORTS

*/

import'nexmo-conversation';

Messaging Service

We’ll need to create an Angular Service to handle the data from our middleware. Let’s generate it using the Angular CLI:

1

2

$nggservice messaging

Two new files were generated in our src/app folder, messaging.service.spec.ts and messaging.service.spec.ts. We’re going to update the messaging.service.spec.ts in order to add the ConversationClient from the Nexmo In-App SDK, instantiate a client, and handle getting a user JSON Web Token from the middleware. We’re going to replace the boilerplate code with:

We need to update app.module.ts in order to import the MessagingService and register it as a provider:

1

2

3

4

5

6

7

import{MessagingService}from'./messaging.service';

...

providers:[MessagingService],

...

Login Component

Let’s start building some UI for our app. We’ll start with a LoginComponent, and we’ll generate that with the Angular CLI:

1

2

$nggcomponent login

That will generate a login folder inside the app folder, and four files, for the HTML, CSS, and TypeScript code, as well as tests. Let’s replace the code in login.component.html with a UI for logging in. I chose a <mat-grid-list> with a <mat-card> inside of it, and a form with a login button that calls onLogin() when it’s submitted. The code looks like this:

Let’s add some CSS to it for the Material spinner, in the login.component.css file:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

.login{

text-align:center;

}

.mat-spinner{

width:20px!important;

height:20px!important;

display:inline-block;

margin-left:10px;

}

/deep/.mat-spinnersvg{

width:20px!important;

height:20px!important;

}

We also need to update login.component.ts in order to add the MessagingService to it and implement the onLogin() method. The method is going to take the username, make a request via the messaging service to the middleware we are running in order to get a user JWT, and then use that to authenticate via the login method of the Nexmo In-App JavaScript SDK. The code looks like this:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

import{Component,OnInit}from'@angular/core';

import{Router}from'@angular/router';

import{MessagingService}from'../messaging.service';

@Component({

selector:'app-login',

templateUrl:'./login.component.html',

styleUrls:['./login.component.css']

})

exportclassLoginComponentimplementsOnInit{

username:string=""

constructor(privatems:MessagingService,privaterouter:Router){}

ngOnInit(){

this.ms.initialize()

}

onLogin(){

this.ms.getUserJwt(this.username).then(this.authenticate.bind(this))

}

authenticate(userJwt:string){

this.ms.client.login(userJwt).then(app=>{

this.ms.app=app

this.router.navigate(['/conversation']);

})

}

}

Notice that I’ve imported the Router from Angular, injected it into our constructor and I’m using it at the end of the authentication flow to navigate to the next page, /conversation.

Conversation Component

We haven’t actually created the conversation component yet, so let’s go ahead and use the Angular CLI to create that:

1

2

$nggcomponent conversation

We’re going to update the conversation.component.html file to use a material sidenav component, which lists the user conversations on the left and the conversation members on the right, leaving the middle for our main chat. We’ll add a header to the chat section to list the conversation name and member count, and add an input section at the bottom. We’ll leave the middle section for the actual conversation history to be displayed. We’ll build an empty shell for now and add to it later as we develop the ConversationComponent. The HMTL should look like this:

We’re going to update the conversation.component.ts file with the necessary boilerplate for methods we’re going to use later on:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

import{Component,OnInit}from'@angular/core';

import{Router}from'@angular/router';

import{Observable}from'rxjs/Observable';

import'rxjs/add/observable/from';

import'rxjs/add/operator/map';

import{MessagingService}from'../messaging.service';

@Component({

selector:'app-conversation',

templateUrl:'./conversation.component.html',

styleUrls:['./conversation.component.css']

})

exportclassConversationComponentimplementsOnInit{

constructor(privatems:MessagingService,privaterouter:Router){}

buildConversationsArray(conversations){

}

ngOnInit(){

}

selectConversation(conversationId:string){

}

sendText(text:string){

}

conversations:any

selectedConversation:any

text:string

events:Array<any>=[]

}

Let’s start by implementing ngOnInit() so that it checks if we have app data before trying to getConversations using the Nexmo In-App SDK. If there is no app data, then we’ll get redirected to the login screen:

1

2

3

4

5

6

7

8

9

10

ngOnInit(){

if(!this.ms.app){

this.router.navigate(['/']);

}else{

this.ms.app.getConversations().then(conversations=>{

this.conversations=this.buildConversationsArray(conversations)

})

}

}

We need to implement a helper method for building a conversations array out of the conversations dictionary the Nexmo In-App JavaScript SDK provides, so we can use it with *ngFor in the UI later on:

1

2

3

4

5

6

7

8

9

10

buildConversationsArray(conversations){

let array=[];

for(let conversation inconversations){

array.push(conversations[conversation]);

}

returnarray

}

Let’s add a method for selecting a conversation from the list. We’ll need to take the ID from the view and pass it on to the controller, and then use the Nexmo SDL to get data about the conversation. We’ll store this in a class property, so it’s available to the view later on. We’re also using Observable to create an array from the conversation.events Map, so we can recreate chat history when the user comes back to the app. We’ll also add an event listener using the SDK to listen for text events and add those to the events history as well:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

selectConversation(conversationId:string){

this.ms.app.getConversation(conversationId).then(conversation=>{

this.selectedConversation=conversation

Observable.from(conversation.events.values()).subscribe(

event=>{

this.events.push(event)

}

)

this.selectedConversation.on("text",(sender,message)=>{

this.events.push(message)

})

console.log("Selected Conversation",this.selectedConversation)

}

)

}

Last but not least, let’s add a method that takes the input from the view and sends it to the Nexmo In-App API via the SDK:

1

2

3

4

sendText(text:string){

this.selectedConversation.sendText(text).then(()=>this.text="")

}

Now that we’ve implemented all the methods we need, we can go back and start to flesh out the view some more, to use the data models we created in the controller. First, let’s update the conversations section in conversation.component.html:

We’re using a pipe called keys here to transform the members dictionary object we get from the SDK into an array, so we’ll need to create that using the Angular CLI and update the generated keys.pipe.ts file:

1

2

$nggpipe keys

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import{Pipe,PipeTransform}from'@angular/core';

@Pipe({

name:'keys'

})

exportclassKeysPipeimplementsPipeTransform{

transform(value,args:string[]):any{

let keys=[];

for(let key invalue){

keys.push({key:key,value:value[key]});

}

returnkeys;

}

}

Next, we’ll update the conversation-header section of the view to display the selected conversation name and member count:

1

2

3

4

5

6

7

8

9

10

11

...

<div class="mat-typography conversation-header">

<h2>

<mat-icon>forum</mat-icon>

{{selectedConversation.display_name}}</h2>

<p>

<mat-icon>account_circle</mat-icon>

{{(selectedConversation.members|keys).length}}Members</p>

</div>

...

We also need to update the conversation-history section in order to parse the events and recreate history in the chat. Events coming from the Nexmo In-App SDK have multiple types, so we’ll account for some of them, like member:joined and text:

We also need to replace the entire app.component.html with the <router-outlet> in order to display the router on the first page:

1

2

<router-outlet></router-outlet>

Run Your App!

After making the app detailed in this post, run the app to see it working:

1

2

$ng serve

The app will run at “http://localhost:4200”. I’d suggest opening the app in two separate tabs, logging in with both alice and jamie and start talking to each other! If you’d like to see the app in its final state, you can check out the source code for this app on our community github page. If you want to see a more advanced version of this code, you can check the stitch-demo middleware code you downloaded at the beginning of the blog post. It also contains an Angular Material front-end.

What’s Next?

If you’d like to continue learning how to use the Nexmo In-App SDK for JavaScript, check out our quickstarts where we show you how to:

Alex Lakatos is a JavaScript Developer Advocate for Nexmo. In his spare time he volunteers at Mozilla as a Tech Speaker and a Reps Mentor. JavaScript developer building on the open web, he has been pushing its boundaries every day. You can check out his github profile or get in touch on twitter. When he’s not programming in London, he likes to travel the world, so it’s likely you’ll bump into him in an airport lounge.

Get the latest posts from Nexmo’s next-generation communications blog delivered to your inbox.

This iframe contains the logic required to handle Ajax powered Gravity Forms.
By signing up to our communications blog, you accept our privacy policy , which sets out how we use your data and the rights you have in respect of your data. You can opt out of receiving our updates by clicking the unsubscribe link in the email or by emailing us at [email protected].

Email*

This iframe contains the logic required to handle Ajax powered Gravity Forms.
By signing up to our communications blog, you accept our privacy policy , which sets out how we use your data and the rights you have in respect of your data. You can opt out of receiving our updates by clicking the unsubscribe link in the email or by emailing us at [email protected].