Build an Angular Video Chat App
Tags: angular, javascript, css, html
Published: 2024-07-05 05:21:02


Popular chat messaging apps like WhatsApp and Telegram offer real-time video calls, while video conferencing apps like Zoom and [Google Meet](https://getstream.io/blog/zoom-clone-compose/) provide group chat during meetings. Chat apps with video calling and video conferencing apps with chat support focus on two similar communication needs.

This article guides you in building an Angular chat app with integrated video conferencing support. You can customize the final project to prioritize video conferencing with chat as a secondary feature.
 
## Prerequisites

The two main features of the sample project in this tutorial are chat messaging and video calling. For messaging functionality, we will use the [Stream Chat Angular SDK](https://getstream.io/chat/sdk/angular/). We will also power video calling support with the above SDK's companion [JavaScript Video SDK](https://getstream.io/video/sdk/javascript/). By integrating these two SDKs as in-apps, developers can build seamless communication experiences, allowing people to send and receive chat messages, livestream, video call, join, and leave live audio room conversations in their apps. 

You can recreate this tutorial’s project with your favorite IDE, preferably [VS Code](https://code.visualstudio.com/). 

## Run the Source Code


![A preview of the chat and video calling app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kj6azfygddj1kv1orv3i.gif)

To test the sample project, you can clone and have a local version or create a GitHub Codespace for the app in this [repo](https://github.com/GetStream/angular-chat-video-demo).


## Start With the Chat Feature

![Chat UI with emojis](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjy1n7dhytgj67q3mp5f.png)


In this section, you will build the Angular chat UI with a look and feel similar to the image above. The leading chat features include instant messaging, media attachments, emojis, threads and replies, and [message interactions](https://getstream.io/chat/docs/sdk/angular/concepts/message-interactions/). Let's initiate a new Angular project and get started with the following steps. 


### Create a New Angular Project

Creating a new Angular project requires the installation of [Node.js](https://nodejs.org/en). Open Terminal or your favorite command line tool and check the version of Node on your machine with the following: 

`node -v`

If you don't have Node installed, click the link above to install the latest stable (LTS) version. 

Execute the following command to install the [Angular CLI](https://www.npmjs.com/package/@angular/cli) globally on your machine. 

`npm install -g @angular/cli`

**Note**: On macOS, you may be required to prepend `sudo` to the above command. 

`sudo npm install -g @angular/cli`

With the Angular CLI installed, you can now create a new project with:

`ng new video-chat-angular --routing false --style css --ssr false` 

where `video-chat-angular` is the project name. For styling, we use ***CSS* and also turn off routing.  You can use whatever name you want for the project. 


Open the app in your IDE and install the necessary dependencies as follows. 

![The project in VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvjpuqfy1u57i0ak9idq.png)


The example above shows the app opened in VS Code. Open the integrated terminal in VS Code and install Stream Chat Angular as a dependency with the following. 

`npm install stream-chat-angular stream-chat @ngx-translate/core`

- `Stream-chat-angular`: [Stream Chat Angular](https://github.com/GetStream/stream-chat-angular) consists of reusable Angular chat components.
- `stream-chat`:  The core Angular SDK without UI components. You can use this to build a completely custom chat messaging experience.
- `ngx-translate/core`: An internationalization (i18n) [library](https://github.com/ngx-translate/core) for Angular projects.


## Configure the Angular Chat SDK

Before we setup the Angular SDK, we should add the required imports in the project's `src/app/app.config.ts` and `src/app/app.component.ts`

Open the file `src/app/app.config.ts` and import the `TranslateModule` in `appConfig` with the following.

`providers: [importProvidersFrom(TranslateModule.forRoot())],`

Next, open `src/app/app.component.ts`, and import the `TranslateModule`, `StreamAutocompleteTextareaModule`, `StreamChatModule` in the `@Component` decorator. 

`imports: [TranslateModule, StreamAutocompleteTextareaModule, StreamChatModule],`

The modified content of this file should look like this: 

```typescript
import { Component, OnInit } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import {
  ChatClientService,
  ChannelService,
  StreamI18nService,
  StreamAutocompleteTextareaModule,
  StreamChatModule,
} from 'stream-chat-angular';

// Video imports
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TranslateModule, StreamAutocompleteTextareaModule, StreamChatModule],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  constructor(
    private chatService: ChatClientService,
    private channelService: ChannelService,
    private streamI18nService: StreamI18nService,
  ) {
    const apiKey = 'REPLACE_WITH_YOUR_STREAM_APIKEY';
    const userId = 'REPLACE_WITH_YOUR_STREAM_USERID';
    const userToken = 'REPLACE_WITH_TOKEN';
    this.chatService.init(apiKey, userId, userToken);
    this.streamI18nService.setTranslation();
  }

  async ngOnInit() {
    const channel = this.chatService.chatClient.channel('messaging', 'talking-about-angular', {
      // add as many custom fields as you'd like
      image: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/2048px-Angular_full_color_logo.svg.png',
      name: 'Talking about Angular',
    });
    await channel.create();
    this.channelService.init({
      type: 'messaging',
      id: { $eq: 'talking-about-angular' },
    });
  }
}
```

In the code above, a valid user should be connected to the SDK's backend infrastructure to access the chat functionality. We are using a hard-coded user credential from our [Angular Chat Tutorial](https://getstream.io/chat/angular/tutorial/). To implement a chat functionality for a production app using this SDK, you should [sign up](https://getstream.io/try-for-free/) for a Dashboard account. On your Stream's Dashboard, you will find your API key to generate a token with our [User JWT Generator](https://getstream.io/chat/docs/react/token_generator/). After filling out the user credentials, we create a new channel and define a condition to populate it when the app runs. 

In addition to the above configurations, the Stream Chat SDK uses TypeScript's concept [Allow Synthetic Default Imports](https://www.typescriptlang.org/tsconfig/#allowSyntheticDefaultImports) to write default  imports in a more efficient way.  Add the following to `tsconfig.json` in the project's root folder to turn it on. 

`"allowSyntheticDefaultImports": true`


## Configure the Chat UI Structure and Style

In our generated Angular project, the UI structure is defined in `src/app/app.component.html`, while its styles are declared in `src/app/styles.css`. Replace the HTML content of `src/app/app.component.html` with the following to load the SDK's chat components such as [Channel](https://getstream.io/chat/docs/sdk/angular/components/ChannelComponent/), [Message List](https://getstream.io/chat/docs/sdk/angular/components/MessageComponent/), [Message Input](https://getstream.io/chat/docs/sdk/angular/components/MessageInputComponent/), and [Thread](https://getstream.io/chat/docs/sdk/angular/components/ThreadComponent/) when the app launches. 


```html
<div id="root">
  <stream-channel-list></stream-channel-list>
  <stream-channel>
    <stream-channel-header></stream-channel-header>
    <stream-message-list></stream-message-list>
    <stream-notification-list></stream-notification-list>
    <stream-message-input></stream-message-input>
    <stream-thread name="thread">
      <stream-message-list mode="thread"></stream-message-list>
      <stream-message-input mode="thread"></stream-message-input>
    </stream-thread>
  </stream-channel>
</div>
```

Let's define the following **CSS** styles in `src/app/styles.css`. The styles we specify here do not include that of the video calling integration. We will modify this file again in a later section to cover that. 


```css
@import 'stream-chat-angular/src/assets/styles/v2/css/index.css';

html {
    height: 100%;
}

body {
    margin: 0;
    height: 100%;
}

#root {
    display: flex;
    height: 100%;

    stream-channel-list {
        width: 30%;
    }

    stream-channel {
        width: 100%;
    }

    stream-thread {
        width: 45%;
    }
}
```


## Test the Chat Functionality

![Chat UI with action menu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qloa5ap0hime8o4tgvot.png)


To start the development server in VS Code to run and test the chat version, launch the integrated Terminal and run: 

`ng serve` or `npm start`


When the server runs successfully, you should see a screen similar to the one below. 


![Server status image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6f95kzziqrzemvq05icz.png)

Open the link to the localhost http://localhost:4200/ in your preferred browser and start testing the chat interface. 


For the rest of this tutorial, let's implement the ability to make video calls by clicking a button at the top-right of the messages list in the chat window. 



## Integrate the Video Calling Functionality


![Chat UI with video calling button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d6vefwgq7gfrwd7h4m6b.png)


To allow users to initiate video calls from the chat UI, we will use Stream Video's [low-level JavaScript client](https://getstream.io/video/docs/javascript/), which developers can implement with any other SDK or platform. 


You can test the app's video calling feature by creating a GitHub Codespace from the [final project](https://github.com/GetStream/angular-chat-video-demo). A Codespace allows you to run and test the app in the browser without cloning and installing it from the GitHub repo.
 
![GitHub Codespaces UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uw92xgk4n5lg698hmg6.png)


## Install and Configure the JavaScript Video SDK

To install the Video SDK, navigate to the project’s root folder in VS Code, open a new Terminal instance and run the following.

`npm i @stream-io/video-client`

Then start the development server with: 

`npm start`


### Create a Video Calling Service

Ensure you are still in the project's `app` folder `/video-chat-angular/src/app`. Then, use the Angular CLI command below to generate a calling service.

`ng generate service calling`

After running the above, you will find two additional generated files When you expand the `app` directory to see its content.

- calling.service.spec.ts
- calling.service.ts

**Note**: The links above do not contain the original generated content. They have been modified to provide our calling service. 

Let's open `calling.service.ts` and substitute what is in the file with the following.

```typescript

import { Injectable, computed, signal } from '@angular/core';
import { Call, StreamVideoClient, User } from '@stream-io/video-client';

@Injectable({
  providedIn: 'root',
})
export class CallingService {
  callId = signal<string | undefined>(undefined);

  call = computed<Call | undefined>(() => {
    const currentCallId = this.callId();
    if (currentCallId !== undefined) {
      const call = this.client.call('default', currentCallId);

      call.join({ create: true }).then(async () => {
        call.camera.enable();
        call.microphone.enable();
      });
      return call;
    } else {
      return undefined;
    }
  });

  client: StreamVideoClient;

  constructor() {
    const apiKey = '5cs7r9gv7sny';
    const token =
      'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZmxvcmFsLW1lYWRvdy01In0.uASF95J3UqfS8zMtza0opjhtGk7r7xW0SJACsHXF0Do';
    const user: User = { id: 'floral-meadow-5' };

    this.client = new StreamVideoClient({ apiKey, token, user });
  }

  setCallId(callId: string | undefined) {
    if (callId === undefined) {
      this.call()?.leave();
    }
    this.callId.set(callId);
  }
}
```

The service we created with the sample code above is designed to manage [video calls](https://getstream.io/video/sdk/javascript/tutorial/video-calling/), including [joining and creating calls](https://getstream.io/video/docs/javascript/guides/joining-and-creating-calls/) with video and audio enabled and leaving calls. We use a reactive programming pattern [Angular Signals](https://angular.dev/guide/signals) to manage state changes reactively. Check out our [YouTube tutorial](https://www.youtube.com/watch?v=lb_6vUfVAr8) to learn more about creating a video calling app with Angular Signals. 

First, we import the JavaScript video SDK, create a video client instance, and initialize it with an API key and token. Check out our documentation's [Client and Authentication](https://getstream.io/video/docs/javascript/guides/client-auth/) section to learn more about user types and how to generate a user token for your projects. 

Finally, we create and join a call if it has not been created using the `call.join` method. The `call.join` method also allows real-time audio and video transmission. 


Next, open `calling.service.spec.ts` and replace the original code with the sample code below. 

```typescript
import { TestBed } from '@angular/core/testing';

import { CallingService } from './calling.service';

describe('CallingService', () => {
  let service: CallingService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(CallingService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});
```

The sample code above performs a basic unit test configuration for the `CallingService`. It checks to see if a calling service can be created or successfully initiated. 


## How to Manage Audio/Video Calls

In this section, we should define a component to manage audio and video call-related functions such as toggling the microphone and camera on and off, accepting and leaving calls, and identifying participants with session IDs. Navigate to the app's `src/app` directory and create a new folder **call**. 


![Image showing project files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9e78vvg6dg42wry7dg49.png)


Add the following files inside the **call** folder and fill out each of their content by copying them from the [GitHub project’s](https://github.com/GetStream/angular-chat-video-demo) links below. 

- [call.component.css](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/call/call.component.css)
- [call.component.html](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/call/call.component.html)
- [call.component.spec.ts](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/call/call.component.spec.ts)
- [call.component.ts](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/call/call.component.ts)

The files above provide the required UI and the logic for managing participants’ states and controlling microphone and camera states during audio/video calls.


## Display and Manage a Call Participant

In this section, we should define a component that sets up the necessary bindings for displaying a participant's video and playing audio in a video call. Let's create the following directory `src/app/participant`. 


![Image showing participant files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r4jwxznu2rn874rxu0pe.png)

Add the participant's files in the image above in the **call** directory and replace each piece of content by copying it from its link to the GitHub project.

- [participant.component.css](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/participant/participant.component.css)
- [participant.component.html](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/participant/participant.component.html)
- [participant.component.spec.ts](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/participant/participant.component.spec.ts)
- [participant.component.ts](https://github.com/GetStream/angular-chat-video-demo/blob/main/src/app/participant/participant.component.ts)


## Add a Start Call Button to the Chat UI


![Chat UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjut65t7y9xwyk9is09u.png)

So far, the header of the chat UI's messages list consists of an avatar and the chat channel's title. Let's modify the message list’s header  to add a **start call** button. We will also implement additional styles for the start button. 

Open `style.css` located in the project's root folder and add this code snippet below the `@import`. 

```css
:root {
  --custom-blue: hsl(218, 100%, 50%);
  --custom-blue-hover: hsl(218, 100%, 70%);

  --custom-red: hsl(0, 80%, 60%);
  --custom-red-hover: hsl(0, 80%, 50%);
}
```

The modified content of `style.css` is shown below. 

```css
@import "stream-chat-angular/src/assets/styles/v2/css/index.css";

:root {
  --custom-blue: hsl(218, 100%, 50%);
  --custom-blue-hover: hsl(218, 100%, 70%);

  --custom-red: hsl(0, 80%, 60%);
  --custom-red-hover: hsl(0, 80%, 50%);
}

html {
  height: 100%;
}

body {
  margin: 0;
  height: 100%;
}

#root {
  display: flex;
  height: 100%;

  stream-channel-list {
    width: 30%;
  }

  stream-channel {
    width: 100%;
  }

  stream-thread {
    width: 45%;
  }
}
``` 

Locate `app.component.css`, an empty CSS file that comes with Angular project generation. Fill out its content with the following style snippet.

```css
.header-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1rem;
}

button {
  background: #005fff;
  color: white;
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
  border: none;
  cursor: pointer;
}
```

Next, let's add the call service to the HTML structure of the chat UI. 

```html
</ng-container>
  <ng-container *ngIf="callingService.callId()">
    <app-call [call]="callingService.call()!"></app-call>
</ng-container>
```
You must add the code snippet above to the `app.component.html` file in the `app` directory. 

```html
<div id="root">
 <stream-channel-list></stream-channel-list>
 <ng-container *ngIf="!callingService.callId()">
 <stream-channel>
 <div class="header-container">
 <stream-channel-header></stream-channel-header>
 <button (click)="startCall()">Start call</button>
 </div>
 <stream-message-list></stream-message-list>
 <stream-notification-list></stream-notification-list>
 <stream-message-input></stream-message-input>
 <stream-thread name="thread">
 <stream-message-list mode="thread"></stream-message-list>
 <stream-message-input mode="thread"></stream-message-input>
 </stream-thread>
 </stream-channel>
 </ng-container>
 <ng-container *ngIf="callingService.callId()">
 <app-call [call]="callingService.call()!"></app-call>
 </ng-container>
</div>
```

Finally, we should modify the `app.component.ts` file that was added in one of the chat sections to import the `CallingService` and `CallComponent` and add a `startCall` method. The modified sample code in `app.component.ts` should look like below. 

```typescript
import { Component, OnInit } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import {
  ChatClientService,
  ChannelService,
  StreamI18nService,
  StreamAutocompleteTextareaModule,
  StreamChatModule,
} from 'stream-chat-angular';
import { CallingService } from './calling.service';
import { CommonModule } from '@angular/common';

// Video imports
import { CallComponent } from './call/call.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    TranslateModule,
    StreamAutocompleteTextareaModule,
    StreamChatModule,
    CommonModule,
    CallComponent,
  ],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  callingService: CallingService;

  constructor(
    private chatService: ChatClientService,
    private channelService: ChannelService,
    private streamI18nService: StreamI18nService,
    callingService: CallingService
  ) {
    this.callingService = callingService;
    const apiKey = '5cs7r9gv7sny';
    const userId = 'floral-meadow-5';
    const userName = 'Floral Meadow';
    const userToken =
      'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZmxvcmFsLW1lYWRvdy01In0.uASF95J3UqfS8zMtza0opjhtGk7r7xW0SJACsHXF0Do';
    this.chatService.init(apiKey, userId, userToken);
    this.streamI18nService.setTranslation();
  }

  async ngOnInit() {
    const channel = this.chatService.chatClient.channel(
      'messaging',
      'talking-about-angular',
      {
        // add as many custom fields as you'd like
        image:
          'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/2048px-Angular_full_color_logo.svg.png',
        name: 'Talking about Angular',
      }
    );
    await channel.create();
    this.channelService.init({
      type: 'messaging',
      id: { $eq: 'talking-about-angular' },
    });
  }

  startCall() {
    const channelId = this.channelService.activeChannel?.id;
    this.callingService.setCallId(channelId);
  }
}
```

## Test the Integrated Chat and Video Calling Features

![A preview of the chat and video calling features](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ck4u4rthp0twi0dmbo12.gif)


Start the development server again with:

`npm start`

The start call button you just implemented will appear at the top right of the message list's header. Clicking the button presents a live video of the local participant. On the live video or active call screen, there are buttons for performing standard call operations like switching the camera and microphone on and off and ending a call. 


## Conclusion

Congratulations! 👏 You have followed all the steps to build a real-time and fully functioning Angular video chat application. The app allows users to send and receive rich text chats, add media as attachments, and connect with others through face-to-face video calling. Explore the related links of this tutorial to learn more about the advanced features and capabilities of the [Angular Chat](https://getstream.io/chat/sdk/angular/) and [JavaScript Video SDKs](https://getstream.io/video/sdk/javascript/).