How to Build a Simple Chat using ASP.NET Core SignalR and Angular 5

Published Nov 16, 2017Last updated Jan 20, 2018

Introduction

ASP.NET Core SignalR is a library for ASP.NET developers that simplifies the process of adding real-time web functionality to applications. Real-time web functionality is the ability to have server code push content to connected clients instantly as it becomes available, and not having the server wait for a client to ask for new data.

An obvious first example is a chat. But you can do tons of stuff with it: real-time collaboration, real-time dashboards and charts, scheduler jobs updates, real-time games.

SignalR will use HTML5 WebSocket API and will try to keep persistent full-duplex communication channel that operates through a single socket. If the browser does not support WebSocket, it will fallback to using another transport like Server Sent Events or Long Polling.

SignalR provides a bridge between server and client. From server-side, it provides a simple API that we can use to call JavaScript functions on client-side from our server-side code. From client-side, it also provides API that we can use to invoke methods on server-side. Therefore, SignalR provides both server-side (NuGet package) library and client side library (npm package).

I blogged before about SignalR. I showed how we can make a simple chat with ASP.NET Core SignalR and vanilla JavaScript. It takes about 5-10 mins for someone who is familiar with the environment and ASP.NET Core. Or even less.

The example was done using ASP.NET Core SignalR and Razor Pages. If you are wondering what are Razor Pages, it is a new feature of MVC that shipped with ASP.NET Core 2. You can read more about it here.

In this post, we will use only bare minimum of the ASP.NET Core with Angular 5. We will use command line prompt with dotnet CLI and Angular CLI.

Setting up the projects and dependencies

Creating the projects

We will create a new empty ASP.NET Core Web project. You can either do it with Visual Studio or execute** dotnet new web** in the command line.

I have Angular CLI installed on my machine. If you don't, either install it or create a new empty Angular application. I am using Angular CLI 1.5 and creating a new project with it - Angular 5 application.

I will just execute _ng new CodingBlastChat _in the command line, inside of solution folder. And now I have basic working Angular application. To start it, I just type in ng serve and I my application is running on localhost port 4200.

Installing dependencies

We need to install both server-side and client-side libraries for ASP.NET Core SignalR.

To install the server-side library we will use NuGet. You can either use Visual Studio or do it via command line. The package name is Microsoft.AspNetCore.SignalR

dotnet add package Microsoft.AspNetCore.SignalR

We will use npm to add client-side library:

npm install @aspnet/signalr-client

If you are using npm 4 or older you will need to add the --save argument, if you want it to be saved inside of your package.json as well. And that's it for library dependencies. We are all set and we can now use SignalR.

Setting up server-side

We can now add the simple ChatHub class:

{
     public void SendToAll(string name, string message)
     {
        Clients.All.InvokeAsync("sendToAll", name, message);
     }
} 

This will call the sendToAll client method for ALL clients.

For SignalR to work we have to add it to DI Container inside of ConfigureServices method in Startup class:

services.AddSignalR();

Also, we have to tell the middleware pipeline that we will be using SignalR. When the request comes to the /chat endpoint we want our ChatHub to take over and handle it.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>(chat);
    });
}

Your Startup class should look something like this.

Enabling CORS

Since we will be serving the Angular application on a separate port, for it to be able to access the SignalR server we will need to enable CORS on the Server.

Add the following inside of ConfigureServices, just before the code that adds SignalR to DI container.

services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
    {
        builder
        .AllowAnyMethod()
        .AllowAnyHeader()
        .WithOrigins("http://localhost:4200");
}));

We also have to tell the middleware to use this CORS policy. Add the following inside of Configure method, BEFORE SignalR:

app.UseCors("CorsPolicy");

Now your Configure method should look like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors("CorsPolicy");

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>("chat");
    });
}

Also, make sure to check your Properties/launchSettings.json file so you can know which port is your app running. You can also configure it to use any port you want. I will set it to 5000.

Client-side

You would ideally want to have a separate service for communicating with ChatHub on the server. Also, you would want to store your endpoints in some kind of Angular service for constants. But for the simplicity sake, we will skip that for now and add it after we make the chat functional.

I will use existing AppComponent that Angular CLI created, and extend it.

I will add properties for nick, message and list of messages. Also, I will add a property for HubConnection.

import { Component } from '@angular/core';
import { HubConnection } from '@aspnet/signalr-client';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private _hubConnection: HubConnection;
  nick = '';
  message = '';
  messages: string[] = [];
}

HubConnection is part of the signalr-client library built by ASP.NET team. And we will use it to establish the connection with the server and also to send messages and listen for messages from the server.

We will establish the connection before any other code runs in our component. Hence, we will use the OnInit event.

import { Component, OnInit } from '@angular/core';
import { HubConnection } from '@aspnet/signalr-client';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  private _hubConnection: HubConnection;
  nick = '';
  message = '';
  messages: string[] = [];

  ngOnInit() {
    this.nick = window.prompt('Your name:', 'John');

    this._hubConnection = new HubConnection('http://localhost:5000/chat');

    this._hubConnection
      .start()
      .then(() => console.log('Connection started!'))
      .catch(err => console.log('Error while establishing connection :('));

    }
}

Notice the ngOnInit method. We are asking the user to enter his name and we store that inside of nick property that we created previously.

After that, we create the HubConnection object and try to establish the connection with the server.

Inside of that method, we will also add the listener for sendToAll event from the server:

this._hubConnection.on('sendToAll', (nick: string, receivedMessage: string) => {
  const text = `${nick}: ${receivedMessage}`;
  this.messages.push(text);
});

After the event is received, we get two parameters: nick and the message itself. Now we form the new string from these two parameters and we add it to our messages array on AppComponent.

Inside of AppComponent we also need a method for sending messages from client TO server. We will use it from our view and here is the code:

 public sendMessage(): void {
    this._hubConnection
      .invoke('sendToAll', this.nick, this.message)
      .catch(err => console.error(err));
}

View

Now we need to set up the view. Since we plan to use the form element, we will import FormsModule in our AppModule. We will change the app.module.ts file. You can see the commit here.

We can now add the view to app.component.html:

<div id="main-container" style="text-align:center">
  <h1>
    <a href="https://codingblast.com/asp-net-core-signalr-simple-chat/" target="_new">
      ASP.NET Core SignalR Chat with Angular
    </a>
  </h1>

  <div class="container">
    <h2>Hello {{nick}}!</h2>
    <form (ngSubmit)="sendMessage()" #chatForm="ngForm">
      <div>
        <label for="message">Message</label>
        <input type="text" id="message" name="message" [(ngModel)]="message" required>
      </div>
      <button type="submit" id="sendmessage" [disabled]="!chatForm.valid">
        Send
      </button>
    </form>
  </div>

  <div class="container" *ngIf="messages.length > 0">
    <div *ngFor="let message of messages">
      <span>{{message}}</span>
    </div>
  </div>

</div>

The view has two main parts.

First is a container for sending messages with a form that consists of input and button for sending the message.

The second part is for listing the messages that we store inside of messages property on AppComponent. We push a new message to this array every time we get an event (message) from the ASP.NET Core SignalR server.

That's all there is to it!

Code Sample

You can find the code sample on GitHub: ASP.NET Core SignalR Chat

You can find step by step (commits) on this repository: AngularAspNetCoreSignalR

Discover and read more posts from Ibrahim Šuta
get started