Using SignalR with Angular

using SignalR in Dotnet core with Angular

In this article I will be showing you how to use SignalR in Dotnet core with angular.

Question – Is it possible to build a chat application in DotNet ?

Answer – Yes. You need to use SignalR library for it.

What is SignalR?

SignalR is a Dotnet library that enables us to have 2-way communication between client and server.

Usually what happens is a client sends a request to the server. The server returns a response. But what if the server wants to send something to client without the client requesting it. For example a notification.

That is where you can use SignalR. SignalR can be used for multiple purposes. For example, building a chat application or sending a notification to all online users. Let’s start

Create a new project and Add dependencies

Create a new project in Dotnet Core. In this tutorial, I have used Angular template in asp.net core web application.

Dependencies-

1) Add the following NuGet package to your project.

Microsoft.AspNetCore.SignalR

2) Install following npm package in your angular project.

@aspnet/signalr

Update Startup.cs class

Add the following in your ConfigureServices method in Startup.cs class.

services.AddSignalR();

Create another endpoint in the Configure method of your Startup.cs class.

  app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");
                //Added
                endpoints.MapHub<BroadcastHub>("/chat");
            });

Add model for message

Add the following model in your project.

public class MessageModel
    {
        public int userId { get; set; }
        public string message { get; set; }
    }

Create the main Hub class for sending messages.

Create the following class in your Dotnet core project and add the method to it. This API is used for 2 way communication. This class inherits from SignalR Hub.

The method broadcasts our message to all users who are online. You can Map the message to a particular user from both the client-side and server-side. In this tutorial, I will show you to do it from client-side. You can learn to do it from the server-side from here.

 public class BroadcastHub : Hub
    {
        public async Task SendMessage(MessageModel message)
        {
            //This method broadcasts the request received from client to all the           connected users
            await Clients.Others.SendAsync("NewMessage", message);
        }
    }

Angular changes

Create the following model inside your angular project. This model will be used to send and receive data from SignalR server API.

export class MessageModel {
  constructor(
    public userId: number,
    public message: string
  ) { }
}

Create a service for communication.

Create a service inside your angular project. I have named it ConnectionRequestService.

I have defined a subject to emit new values whenever received. You can learn more about subject from here. BaseUrl is the URL of our server API. I have registered several events in the constructor.

1)Creating connection with the hub on the server .

2)Listening for incoming request and emitting new data through subject.

3) Start connection.

I have also defined a SendMessage() method which we will call from our component to send message to our server API.

import { EventEmitter, Inject, Injectable } from '@angular/core';
import { HubConnection } from '@aspnet/signalr/dist/esm/HubConnection';
import { HubConnectionBuilder } from '@aspnet/signalr/dist/esm/HubConnectionBuilder';
import { Subject } from 'rxjs';
import { MessageModel } from '../Models/MessageModel';

@Injectable({
  providedIn: 'root'
})
export class ConnectionRequestService {

  BaseUrl: string;
  MessageSubject = new Subject<MessageModel>();
  private Connection: HubConnection;

  constructor(@Inject('BASE_URL') baseUrl: string) {
    this.BaseUrl = baseUrl;

    //create connection
    this.Connection = new HubConnectionBuilder()
      .withUrl(this.BaseUrl + 'chat')
      .build();

    //listen for incoming request
    this.Connection.on('NewMessage', (data: any) => {
      this.MessageSubject.next(data);
    });

    //start connection
    this.Connection.start()
  }

  sendMessage(Message: MessageModel) {
    this.Connection.invoke('SendMessage', Message);
  }
}

Create Components

Add sender component

Create two components inside your angular project. A sender component and a receiver component. Add the following HTML code to your sender component.

Here we are creating a two-way binding for Message property of our class. On click of button SendMessage() method of our class will be called.

<input type="text" [(ngModel)] ="Message" />
<button (click)="SendMessage()">Send</button>

Add the following code to your sender component class. Here we have defined SendMessage() method, which calls ConnectionService to send our message to server API.

import { Component, OnInit } from '@angular/core';
import { MessageModel } from '../Models/MessageModel';
import { ConnectionRequestService } from '../service/connection-request.service';

@Component({
  selector: 'app-sender-component',
  templateUrl: './sender-component.component.html',
  styleUrls: ['./sender-component.component.css']
})
export class SenderComponent implements OnInit {

  Message: string;
  constructor(private ConnectionService: ConnectionRequestService) { }

  ngOnInit() {
    
  }

  SendMessage() {
    this.ConnectionService.sendMessage(new MessageModel(1, this.Message));
  }

}

Create receiver component

Add the following HTML code to the receiver component.

<h1>{{ Message }}</h1>

Add the following code to the receiver component class. In the ngOnInit() method we have defined a listener that is listening to any new values emitted by our subject.

You can learn more about the subject from here. After receiving a new value, we are comparing the userid with our current user id. If it matches, we show the message.

Here we are identifying to which user the message belongs to from the client-side. But the same can also be done from the server-side. You can learn it from here.

import { Component, NgZone, OnInit } from '@angular/core';
import { MessageModel } from '../Models/MessageModel';
import { ConnectionRequestService } from '../service/connection-request.service';

@Component({
  selector: 'app-receiver-component',
  templateUrl: './receiver-component.component.html',
  styleUrls: ['./receiver-component.component.css']
})
export class ReceiverComponent implements OnInit {

  UniqueUserId: number = 1;
  Message: string;
  constructor(private ConnectionService: ConnectionRequestService, private _ngZone: NgZone) { }

  ngOnInit() {

    this.ConnectionService.MessageSubject.subscribe({
      next: (request: MessageModel) => {
        this._ngZone.run(() => {
          if (request.userId == this.UniqueUserId) {
            this.Message = request.message;
          }
        });
      }
    });
  }

}

Update routes

Finally, Define the path in your routing file.

RouterModule.forRoot([
      { path: '', component: HomeComponent, pathMatch: 'full' },
      { path: 'SenderComponent', component: SenderComponent },
      { path: 'ReceiverComponent', component: ReceiverComponent },
    ])

Output :

The code for this tutorial is available on Github at https://github.com/juzer-hakimji/SignalRDemo

Thank you for reading this article. Have a good day.

Share :

Leave a Comment

Your email address will not be published. Required fields are marked *