Getting Started With SignalR Using ASP.NET Core And Angular

Introduction

This article is part of a series called "Getting started with SignalR using ASP.NET Core". In this article, we'll learn how to get started step by step with SignalR using angular 5. At the time of writing article .NET Core 2.1 has been released.

This is part of series of post on SignalR using Angular5 and ASP.NET Core.

  • Overview of New stack SIgnalR on ASPNET Core here.
  • Working with streaming data
  • Working with Azure service
  • Working with Xamarin
  • Working with dynamic hubs 

This article demonstrates the following tasks,

  • Creating Angular SignalR service
  • Creating SignalR Hub
  • Creating ChatRoom component

Prerequisite

You must have the following software.

The source code is available at GitHub.

At the end of this article, you will be able to use SignalR in your project.

 Demo Screen 1
 ASP.NET Core
 
Demo Screen 2
ASP.NET Core 

Why Angular?

This is a hot and trending client-side JavaScript technology. It has many advantages over the client side. Everyone wants the combination of Angular and ASPNET Core so I've decided to do that.

If you want to upgrade your Angular application to the latest version. Upgrade guide: https://update.angular.io/

Let's get started to kick off the demo. We are going to create new Angular SPA(Single Page Application) template project.

Create New Project : File --> New --> Project

ASP.NET Core 

Choose the template: You have to choose Angular SPA template.

.NETCore --> ASP.NET Core 2.1 -- > Angular

ASP.NET Core 

You have created an Angular template project successfully. 

SignalR Startup Configuration

You need to configure SignalR service in Configure Service Section and Map the hub in configure section. It automatically adds the SignalR reference for you.

Add SignalR service in Configure service method below.

  1. // This method gets called by the runtime. Use this method to add services to the container.  
  2.         public void ConfigureServices(IServiceCollection services)  
  3.         {  
  4.             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);  
  5.   
  6.             // In production, the Angular files will be served from this directory  
  7.             services.AddSpaStaticFiles(configuration =>  
  8.             {  
  9.                 configuration.RootPath = "ClientApp/dist";  
  10.             });  
  11.   
  12.             services.AddSignalR();  
  13.         }  

Map the hub in Http pipeline in Configure method

  1. app.UseSignalR(route =>   
  2.     {  
  3.         route.MapHub<ChatHub>("/chathub");  
  4.     });  
 After Configuring SignalR in Startup. We need to install SignalR Javascript library using NPM Package Manager. Run the below command in Command Prompt

Command

  • npm init -y
  • npm install @aspnet/signalr

 npm init to initialize or set the project npm package. -y | --yes to skip the questionnaire.

You have to run the above commands in Package manager console (Tools --> NuGet Package Manager --> Package Manager Console)

Creating SignalR Hub: to establish communication between client and server. You can call any client method via Hub and vice versa. I'm going to create a chathub class in a folder called 'Hubs'. Add reference of SignalR namespace 'Microsoft.AspNetCore.SignalR' in ChatHub class.

  1. public class ChatHub : Hub  
  2.     {  
  3.         public async Task SendMessage(ChatMessage message)  
  4.         {  
  5.             await Clients.All.SendAsync("ReceiveMessage", message);  
  6.         }  
  7.     }  

Creating SignalR Angular Service: This service will take care of creating connection , starting connection and register ingthe server events to receive the messages. Create folder called 'Services' ClientApp --> src --> app --> Services . Create a service typescript class file named 'signalR.service.ts' . I'm using two model classes which are shown below

signalR.service.ts

  1. import { EventEmitter, Injectable } from '@angular/core';  
  2. import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr';  
  3. import { ChatMessage } from '../Models/chatmessage.model';  
  4.   
  5. @Injectable()  
  6. export class SignalRService {  
  7.   messageReceived = new EventEmitter<ChatMessage>();  
  8.   connectionEstablished = new EventEmitter<Boolean>();  
  9.   
  10.   private connectionIsEstablished = false;  
  11.   private _hubConnection: HubConnection;  
  12.   
  13.   constructor() {  
  14.     this.createConnection();  
  15.     this.registerOnServerEvents();  
  16.     this.startConnection();  
  17.   }  
  18.   
  19.   sendChatMessage(message: ChatMessage) {  
  20.     this._hubConnection.invoke('SendMessage', message);  
  21.   }  
  22.   
  23.   private createConnection() {  
  24.     this._hubConnection = new HubConnectionBuilder()  
  25.       .withUrl(window.location.href+'chathub')  
  26.       .build();  
  27.   }  
  28.   
  29.   private startConnection(): void {  
  30.     this._hubConnection  
  31.       .start()  
  32.       .then(() => {  
  33.         this.connectionIsEstablished = true;  
  34.         console.log('Hub connection started');  
  35.         this.connectionEstablished.emit(true);  
  36.       })  
  37.       .catch(err => {  
  38.         console.log('Error while establishing connection, retrying...');  
  39.         setTimeout(this.startConnection(), 5000);  
  40.       });  
  41.   }  
  42.   
  43.   private registerOnServerEvents(): void {  
  44.     this._hubConnection.on('ReceiveMessage', (data: any) => {  
  45.       this.messageReceived.emit(data);  
  46.     });  
  47.   }  
  48. }  

I'd always prefer to create models/poco so let's create ChatMessage and Tab models . Create a new folder called 'Models'.

chatmessage.model.ts class : to contains the detail of chat message like message , room and user info.

  1. /** represent chat message class */  
  2. export class ChatMessage {  
  3.   
  4.   user: string;  
  5.   message: string;  
  6.   room: string;  
  7.   
  8.   constructor(user: string = '', message: string='',room:string='') {  
  9.     this.user = user;  
  10.     this.message = message;  
  11.     this.room = room;  
  12.   }  
  13. }  

tab.model.ts class to contains the detail of each tabs like heading, title and messages history

  1. import { ChatMessage } from '../Models/chatmessage.model';  
  2.   
  3. /** Represent Tab class */  
  4. export class Tab {  
  5.   messageHistory: ChatMessage[];  
  6.   heading: string;  
  7.   title: string;  
  8.   
  9.   constructor(  
  10.     heading: string='',  
  11.     title:string=''  
  12.   )  
  13.   {  
  14.     this.heading = heading;  
  15.     this.title = title;  
  16.     this.messageHistory = [];  
  17.   }  
  18. }  

After creating angular signalR service, now I have to add  providers in app module class. Just import the signalr service and supply to provider so that I can access it in the entire app.

Now I'm going to consume SignalR service into the home component which takes care of the UI like sent message and receive messages. By default, it's creating two chat rooms, like Lobby and SignalR room, but you can create more rooms and you can fetch the room details from the server as well. It's up to you, this is for demo purposes.

home.component.ts class

  1. import { Component, NgZone  } from '@angular/core';  
  2.   
  3. import { SignalRService } from '../services/signalR.service';  
  4. import { ChatMessage } from '../Models/chatmessage.model';  
  5. import { Tab } from '../Models/tab.model';  
  6.   
  7. @Component({  
  8.   selector: 'app-home',  
  9.   templateUrl: './home.component.html',  
  10. })  
  11.   
  12. export class HomeComponent {  
  13.   chatMessage: ChatMessage;  
  14.   canSendMessage: boolean;  
  15.   tabs: Tab[];  
  16.   currentRoom: string;  
  17.   
  18.   constructor(  
  19.     private signalrService: SignalRService,  
  20.     private _ngZone: NgZone  
  21.   )  
  22.   {  
  23.     this.subscribeToEvents();  
  24.     this.chatMessage = new ChatMessage();  
  25.     this.tabs = [];  
  26.     this.tabs.push(new Tab('Lobby''Welcome to lobby'));  
  27.     this.tabs.push(new Tab('SignalR''Welcome to SignalR Room'));  
  28.     this.currentRoom = 'Lobby';  
  29.   }  
  30.   
  31.   sendMessage() {  
  32.     if (this.canSendMessage) {  
  33.       this.chatMessage.room = this.currentRoom;  
  34.       this.signalrService.sendChatMessage(this.chatMessage);  
  35.     }  
  36.   }  
  37.   
  38.   OnRoomChange(room) {  
  39.     this.currentRoom = room;  
  40.   }  
  41.   
  42.   private subscribeToEvents(): void {  
  43.     this.signalrService.connectionEstablished.subscribe(() => {  
  44.       this.canSendMessage = true;  
  45.     });  
  46.   
  47.     this.signalrService.messageReceived.subscribe((message: ChatMessage) => {  
  48.       this._ngZone.run(() => {  
  49.         this.chatMessage = new ChatMessage();  
  50.         let room = this.tabs.find(t => t.heading == message.room);  
  51.         if (room) {  
  52.             room.messageHistory.push(new ChatMessage(message.user, message.message, message.room));  
  53.         }  
  54.       });  
  55.     });  
  56.   }  
  57. }  

home.component.html 

  1. <ul class="nav nav-tabs">  
  2.   <li *ngFor="let tab of tabs" [ngClass]="{'active': currentRoom==tab.heading}" (click)="OnRoomChange(tab.heading)"><a data-toggle="tab" href="#home">{{tab.heading}}</a></li>  
  3. </ul>  
  4.   
  5. <div class="tab-content">  
  6.   <div *ngFor="let tab of tabs" id="home" class="tab-pane fade in" [ngClass]="{'active': currentRoom==tab.heading}">  
  7.     <h3>{{tab.title}}</h3>  
  8.     <input type="hidden" name="room" [(ngModel)]="chatMessage.room" value="{{tab.heading}}" />  
  9.     <p *ngFor="let msg of tab.messageHistory">{{msg.message}}</p>  
  10.   </div>  
  11. </div>  
  12.   
  13. <form class="form-inline" (ngSubmit)="sendMessage()">  
  14.   <footer class="footer" style="width:70%;background:#fdd679; bottom:0%;position:fixed">  
  15.     <div class="container">  
  16.       <div class="row" style="background:#fdd679;">  
  17.         <div class="col-sm-9">  
  18.   
  19.           <div style="width:100%;  padding-top: 10px;" class="clearfix">  
  20.             <input type="text" style="width:50%;padding:0px 2%;margin-bottom:5px; float:left; height:32px;" type="text" id="message" [(ngModel)]="chatMessage.message" name="message" class="form-control pull-left shadow" input-focus="content" />  
  21.   
  22.             <input style="width:26%;padding:6px 0px;text-align:center;margin-bottom:5px; float:right; border-radius:3px; background:#e9a403;" id="sendmessage" class="btn btn-default pull-right shadow" type="submit" value="Send" />  
  23.           </div>  
  24.         </div>  
  25.       </div>  
  26.     </div>  
  27.   </footer>  
  28. </form>  

Finally, we have completed the working Chat room demo with SignalR using ASPNET Core and Angular 5. Source code of this project is available @ Github, fork and you are free to play with it.

The source code @ Github - https://github.com/nemi-chand/ASPNETCore-SignalR-Angular-TypeScript

Summary

In this we have seen the working demo of a new signalR stack on ASPNET Core using Angular 5 SPA template, also we have seen Angular signalR service to communicate with the hub. This is for demonstration purposes only, you may modify as per your requirements. I hope you have enjoyed this article and don't forgot to comment.

References