In this article, we will learn the azure communication chat using Angular and generate tokens in the back-end ASP.Net Core.
First of all, we have to create a communication service resource from here and keep it private.
Backend:
After generating the resource connection string, we have to create 2 APIs in the backend for token generation.
- Create a User and generate a token
- Generate token from User Id
Install the following NuGet packages:
- Azure.Communication.Common
- Azure.Communication.Identity
Add connection string in appsettings.json :
"ResourceConnectionString": "endpoint=https://test.westus.communication.azure.com/;accesskey=SAMPLE KEY 1234"
Add new API Controller :
using System; using System.Threading.Tasks; using Azure; using Azure.Communication; using Azure.Communication.Identity; using Azure.Core; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; namespace Calling { public class UserTokenController : Controller { private readonly CommunicationIdentityClient _client; public UserTokenController(IConfiguration configuration) { _client = new CommunicationIdentityClient(configuration["ResourceConnectionString"]); } /// <summary> /// Create User and Generate token /// </summary> /// <returns></returns> [Route("/token")] [HttpGet] public async Task<IActionResult> GetAsync() { try { Response<CommunicationUserIdentifierAndToken> response = await _client.CreateUserAndTokenAsync(scopes: new[] { CommunicationTokenScope.Chat }); var responseValue = response.Value; var jsonFormattedUser = new { communicationUserId = responseValue.User.Id }; var clientResponse = new { user = jsonFormattedUser, token = responseValue.AccessToken.Token, expiresOn = responseValue.AccessToken.ExpiresOn }; return this.Ok(clientResponse); } catch (RequestFailedException ex) { Console.WriteLine($"Error occured while Generating Token: {ex}"); return this.Ok(this.Json(ex)); } } /// <summary> /// Generate token for the User /// </summary> /// <returns></returns> [Route("/refreshToken/{identity}")] [HttpGet] public async Task<IActionResult> GetAsync(string identity) { try { CommunicationUserIdentifier identifier = new CommunicationUserIdentifier(identity); Response<AccessToken> response = await _client.GetTokenAsync(identifier, scopes: new[] { CommunicationTokenScope.Chat }); var responseValue = response.Value; var clientResponse = new { token = responseValue.Token, expiresOn = responseValue.ExpiresOn }; return this.Ok(clientResponse); } catch (RequestFailedException ex) { Console.WriteLine($"Error occured while Generating Token: {ex}"); return this.Ok(this.Json(ex)); } } } }
Frontend:
Install the following packages in angular:
npm install @azure/communication-common --save npm install @azure/communication-identity --save npm install @azure/communication-signaling --save npm install @azure/communication-chat --save
create new component azure-chat.component.ts :
import { Component, OnInit } from '@angular/core'; import { ChatClient, ChatThreadClient } from '@azure/communication-chat'; import { HttpClient } from '@angular/common/http'; import { AzureCommunicationTokenCredential } from '@azure/communication-common'; @Component({ selector: 'app-azure-chat', templateUrl: './azure-chat.component.html', styleUrls: ['./azure-chat.component.css'] }) export class AzureChatComponent implements OnInit { endpointUrl = 'https://test.westus.communication.azure.com'; communicationUserId: string = ''; userAccessToken: string = ''; chatClient: ChatClient | any = null; topicName: string = ''; displayName: string = ''; threadLink: string = ''; chatThreadClient: ChatThreadClient | any = null; otherCommunicationUserId: string = ''; otherDisplayName: string = ''; constructor(public http: HttpClient) { } ngOnInit(): void { // https://localhost:44316/token -----> For create an User and Generate Token // https://localhost:44316/refreshToken/{identity} -----> For Generate Token this.http.get('https://localhost:44316/token').subscribe((data: any) => { this.userAccessToken = data.token; this.communicationUserId = data.user.communicationUserId; this.chatClient = new ChatClient(this.endpointUrl, new AzureCommunicationTokenCredential(this.userAccessToken)); }); } async createChatThread() { let createChatThreadRequest = { topic: this.topicName }; let createChatThreadOptions = { participants: [ { id: { communicationUserId: this.communicationUserId }, displayName: this.displayName } ] }; let createChatThreadResult = await this.chatClient.createChatThread( createChatThreadRequest, createChatThreadOptions ); let threadId = createChatThreadResult.chatThread.id; console.log(`Thread created:${threadId}`); this.threadLink = 'http://localhost:4200/chat/' + threadId; this.chatThreadClient = this.chatClient.getChatThreadClient(threadId); } async AddUserParticipantToTheChatThread() { let addParticipantsRequest = { participants: [ { id: { communicationUserId: this.otherCommunicationUserId }, displayName: this.otherDisplayName } ] }; let addUser = await this.chatThreadClient.addParticipants(addParticipantsRequest); console.log(addUser); } async listAllChatThreads() { let threads = this.chatClient.listChatThreads(); for await (const thread of threads) { console.log(thread); } } }
azure-chat.component.html :
<label for="">Add Thread</label> <br><br> <input type="text" placeholder="Topic Name" [(ngModel)]="topicName" name="topicName"> <input type="text" placeholder="Your Display Name" [(ngModel)]="displayName" name="displayName"> <button (click)="createChatThread()">Submit</button> <br><br> <a *ngIf="threadLink" href="{{threadLink}}">{{threadLink}}</a> <br><br><br><br><br> <div *ngIf="threadLink"> <label for="">Add user to thread</label> <br><br> <input type="text" placeholder="User id" [(ngModel)]="otherCommunicationUserId" name="otherCommunicationUserId"> <input type="text" placeholder="Display Name" [(ngModel)]="otherDisplayName" name="otherDisplayName"> <button (click)="AddUserParticipantToTheChatThread()">Submit</button> </div>
create new component chat-thread.component.ts :
import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; import { ChatClient, ChatThreadClient } from '@azure/communication-chat'; import { HttpClient } from '@angular/common/http'; import { AzureCommunicationTokenCredential } from '@azure/communication-common'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-chat-thread', templateUrl: './chat-thread.component.html', styleUrls: ['./chat-thread.component.css'] }) export class ChatThreadComponent implements OnInit { endpointUrl = 'https://test.westus.communication.azure.com'; communicationUserId: any; userAccessToken: string = ''; chatClient: ChatClient | any = null; topicName: string = ''; displayName: string = ''; currentThreadId: any; chatThreadClient: ChatThreadClient | any = null; messages: message[] = []; sendMsg: message = { message: '', sender: '' }; constructor(public http: HttpClient, private changeDetection: ChangeDetectorRef, private route: ActivatedRoute) { } ngOnInit(): void { this.currentThreadId = this.route.snapshot.paramMap.get('threadid'); this.communicationUserId = this.route.snapshot.paramMap.get('userid'); this.http.get('https://localhost:44316/refreshToken/' + this.communicationUserId).subscribe((data: any) => { this.userAccessToken = data.token; //this.communicationUserId = data.user.communicationUserId; this.chatClient = new ChatClient(this.endpointUrl, new AzureCommunicationTokenCredential(this.userAccessToken)); this.chatThreadClient = this.chatClient.getChatThreadClient(this.currentThreadId); this.setupHandlers(); }); } async setupHandlers() { this.getListMessages(); await this.chatClient.startRealtimeNotifications(); this.chatClient.on("chatMessageReceived", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("chatMessageEdited", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("chatMessageDeleted", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("typingIndicatorReceived", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("readReceiptReceived", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("chatThreadCreated", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("chatThreadDeleted", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("chatThreadPropertiesUpdated", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("participantsAdded", ((state: any) => { this.getListMessages(); }).bind(this)); this.chatClient.on("participantsRemoved", ((state: any) => { this.getListMessages(); }).bind(this)); } async getListMessages() { this.messages = []; const messages = this.chatThreadClient.listMessages(); for await (const message of messages) { if (message.type == "text") { let msg: message = { sender: message.senderDisplayName, message: message.content.message }; this.messages.push(msg); } } this.changeDetection.detectChanges(); } async sendMessage() { let sendMessageRequest = { content: this.sendMsg.message }; let sendMessageOptions = { senderDisplayName: this.sendMsg.sender, type: 'text' }; const sendChatMessageResult = await this.chatThreadClient.sendMessage(sendMessageRequest, sendMessageOptions); const messageId = sendChatMessageResult.id; this.sendMsg = { message: '', sender: '' }; } async ListUsersInChatThread() { const participants = this.chatThreadClient.listParticipants(); for await (const participant of participants) { console.log(participant); } } async RemoveUserFromChatThread(userId: any) { await this.chatThreadClient.removeParticipant({ communicationUserId: userId }); await this.ListUsersInChatThread(); } } interface message { sender: string, message: string }
chat-thread.component.html :
<div *ngFor="let message of messages;" class="row"> <label for="">{{message.sender}}</label> : <label for="">{{message.message}}</label> </div> <br><br><br><br> <div class="col-md-12"> <label for="">Send New Message</label> <br><br> <input type="text" placeholder="Sender" [(ngModel)]="sendMsg.sender" name="sender"> <input type="text" placeholder="Message" [(ngModel)]="sendMsg.message" name="message"> <button (click)="sendMessage()">Submit</button> </div>
app-routing.module.ts :
import { ChatThreadComponent } from './chat/chat-thread/chat-thread.component'; import { AzureChatComponent } from './chat/azure-chat/azure-chat.component'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', component: AzureChatComponent }, { path: 'chat/:threadid/:userid', component: ChatThreadComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
app.module.ts :
import { HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AzureChatComponent } from './chat/azure-chat/azure-chat.component'; import { ChatThreadComponent } from './chat/chat-thread/chat-thread.component'; @NgModule({ declarations: [ AppComponent, AzureChatComponent, ChatThreadComponent ], imports: [ FormsModule, HttpClientModule, BrowserModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Now you have to run Angular and API both projects and Assume the following ports:
- Angular : http://localhost:4200/
- Asp.Net API : https://localhost:44316/
Outputs :
- Create user :
- Create Chat thread and Add Users :
- Redirect to a Particular chat thread and Communicate with participants :
You can gift me a coffee from this link if I saved your valuable time 🙂
This example getting all the message, how can I make it lazy load with paging?