Tuesday, 24 July 2018

ASP.NET Core SignalR Chat with Angular 5

In one of the previous posts, we saw how we can make a simple chat with ASP.NET Core SignalR. 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 core 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 class ChatHub : Hub
{
public void SendToAll(string name, string message)
{
Clients.All.InvokeAsync("sendToAll", name, message);
}
}
view rawChatHub.cs hosted with ❤ by GitHub
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();
view rawStartup.cs hosted with ❤ by GitHub
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");
});
}
view rawStartup.cs hosted with ❤ by GitHub
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");
}));
view rawStartup.cs hosted with ❤ by GitHub
We also have to tell the middleware to use this CORS policy. Add the following inside of Configure method, BEFORE SignalR:
app.UseCors("CorsPolicy");
view rawStartup.cs hosted with ❤ by GitHub
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");
});
}
view rawStartup.cs hosted with ❤ by GitHub
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[] = [];
}
view rawapp.component.ts hosted with ❤ by GitHub
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 :('));
}
}
view rawapp.component.ts hosted with ❤ by GitHub
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);
});
view rawapp.component.ts hosted with ❤ by GitHub
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 rawapp.component.ts hosted with ❤ by GitHub

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>
view rawapp.component.html hosted with ❤ by GitHub
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

2 comments:

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...