The best way of setting up Zoom inside your Angular app

Alan Acuña
8 min readFeb 16, 2021

Let me tell you a little story. I recently participated in a project where the client needed to integrate Zoom to their platform. According to the client’s requirements, the platform would be made in Angular with at least 4 routes and 5 modal screens needed to open a different Zoom meeting each. I, being in charge of doing the frontend, did this integration by reviewing the Zoom Web SDK docs as well as the example of Angular integration.

The day came to present the platform to the client… and he was completely furious with my team and, above all, with me. And with good reason. I mean, the platform “worked”, but it ran at a snail’s pace and, worst of all, it was super buggy (I’ll specify the details later).

Therefore, my team and I had to come up with a solution for the platform. This solution I now consider the best way to work with the Zoom Web SDK, at least as of this writing. My intention with this article is not to rant against the Zoom Web SDK, but to help all my colleagues who may run into similar project requirements and avoid leaving their clients with a bad taste in their mouths.

What are the issues that come up working with Zoom’s Web SDK?

I have no doubt that the Zoom Web SDK works very well for simple integrations like the one shown in their integration example, but if you require something more complex, you’re going to run into some difficulties. Let’s start by detailing the main issues I ran into when working with Zoom’s Web SDK.

1. It’s hard to customize

When you start the Zoom Web SDK, multiple scripts and elements are added to the DOM. Mainly, a div with the ID “zmmtg-root” is added where the entire Zoom UI is located. When starting a meeting this container covers the entire screen. But what if you want the meeting to look like an embedded video within your site? It should be as simple as specifying the height and width of zmmtg-root with CSS, right?

Unfortunately you will find that something as simple as resizing or hiding an element of the Zoom UI turns into a full DOM inspection that will take all afternoon. And hopefully you are not using Bootstrap on your site, because when loading the Zoom Web SDK, any customization will be completely ignored, be it color or directly to some Bootstrap class (😨). Oh and if you customized the font of the site it will also be ignored, regardless of whether you use Bootstrap or not (😱).

2. Worsens the site’s performance

As I already mentioned, a particular client requirement for their platform was that some of the site’s routes open a different Zoom meeting each. There is nothing special about this requirement and it is very likely that you will come across something similar. Super easy to do with Angular. You can use the same component for all Zoom meetings and indicate with a route parameter which meeting is the one to open.

Of course, the platform doesn’t just have routes to open Zoom meetings. So you can create a module that contains that component and use lazy loading of the module so that it only loads when entering a meeting path. And this is where the problem arises. Simply importing the Zoom Web SDK into the meeting component makes navigating to this component take 2–3 seconds. It does not need to be said, but 3 seconds to change routes in Angular is an eternity 🐌. This simple fact made the entire platform feel very sluggish, and even completely still.

3. Issues to manage multiple meeting

I’m going to be very honest right now. Trying to handle different routes and modal screens with a different Zoom meeting each makes you want to take a shot. And it has to do with the way the Zoom SDK works. Before opening a meeting, JS and WebAssembly scripts from the SDK have to be loaded, but once you have loaded them you cannot remove them unless you reload the site. And if you try to load them again, it throws an error and stops working completely.

Also, to start another meeting you have to leave the previous one. But to test it, you need to have the meetings open, because if you don’t it throws an error and for some reason it won’t let you join other meetings afterwards. And, just to join a meeting you would have to check if the SDK scripts were loaded or not, if there is an open meeting or not, if the user could join the meeting or not. And there are some errors the SDK throws that are impossible to debug, the kind that tells something happened, but doesn’t even tell you what happened.

Simply, trying to use the Zoom Web SDK in a complex application becomes a nightmare. But as I said at the beginning, we found a solution that solves all of the problems that I just described, and that is to use the SDK inside an iframe.

Using Zoom’s Web SDK inside an iframe

To clarify, we are not going to embed Zoom in an iframe as if it were a simple video. You also don’t need to create a separate app with a subdomain or something like that. What we are going to do is create a special Zoom meeting component in the same app and we are going to put the route of this component as the source of the iframe. Yes, I was also confused when my team proposed it to me.

Basic setup

Remember that you can check the result of this tutorial in this repo on GitHub. The first thing we are going to do is create a new Angular project. Next we will install Bootstrap through their CDN. This step, although optional, will demonstrate that this solution solves any customization problems. We are also going to install the Zoom Web SDK, using the command npm install @zoomus/websdk.

To use the SDK we need a little configuration in our angular.json file. The assets and styles sections would look like this:

If you’ve seen Zoom’s Angular integration example, you’ll notice that it has an additional line in the styles section which is: node_modules/@zoomus/websdk/dist/css/bootstrap.css. This line is not necessary in our project because we are already using Bootstrap. If you are not using Bootstrap in your project you must include it.

At this point it is a good idea to create an application in the Zoom Marketplace so that you get your API Key and Secret. Just include your API Key in your environment files. On the other hand, you will need to generate a signature to join a meeting. This is beyond the scope of this article, but you just need to create a very simple endpoint on your backend. Zoom provides examples of this in various languages ​​in its documentation.

Components and routes

In our project we are going to create a navbar.component, a home.component and a meeting.component. The navbar is always shown. The home is not going to do anything, its only purpose is to demonstrate that you can have routes without meetings. It will be the meeting.component that will have the iframe to display the meetings, changing the meeting depending on a path parameter. We’re not going to use lazy loading for the home and meeting routes but this is something you can do without a problem. So far our routes in the app-routing.module would look like this:

const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: ':meeting', component: MeetingComponent },
{ path: '**', pathMatch: 'full', redirectTo: 'home' }
];

We are also going to create a sanitizer pipe that will help us sanitize the URL that we are going to insert into the iframe. This pipe would look like this:

Within the meeting.component, we are going to first obtain the path parameter that will indicate the meeting that should be opened. For this we must subscribe to the params property of ActivatedRoute, because if not, we will not be able to detect the change of route to another meeting.

ngOnInit(): void {
this.route.params.pipe(takeUntil(this.destroy)).subscribe(
({ meeting }) => this.validateMeeting(meeting)
);
}

Here we call a method to validate the meeting identifier and get the active meeting. In this case the validation is done with a local meeting dictionary, but you can create a file with this dictionary or have an endpoint to obtain the meeting’s information. In this example, it would look like this:

private validateMeeting(meetingName: string) {
const meeting = this.meetings.find(meeting => meeting.name === meetingName);
return meeting
? this.setMeetingUrl(meeting)
: this.router.navigateByUrl('/home');
}

Finally, we call the method that creates the URL that we are going to use in the iframe, this way:

private setMeetingUrl(meeting: Meeting) {
this.activeMeeting = meeting;
this.url = `/zoom/${ meeting.zoomId }/${ meeting.zoomPasscode }`;
}

Note that we have not yet defined a route that corresponds to that URL, but we will soon. To show the iframe we just have to add this in the HTML of our component, it is very important that you use the pipe sanitizer at this point.

<iframe [src]="url | sanitizer" frameborder="0" class="w-100 h-100"></iframe>

If all goes well, you should be able to see the same website within the iframe. Now we can continue defining the Zoom module, where all the logic to use the Web SDK will be found.

Zoom module

Let’s create a zoom.module with the — routing flag. This will create a routes file for this module. We are also going to create a zoom.component and a zoom.service. The component can be created with the flags — inline-style — inline-template since it will not have anything in the template. Now we are going to add a route in our app-routing.module and it will be lazy loaded, looking like this:

const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'zoom', loadChildren: () => import('./zoom/zoom.module').then(m => m.ZoomModule) },
{ path: ':meeting', component: MeetingComponent },
{ path: '**', pathMatch: 'full', redirectTo: 'home' }
];

Whereas, inside the zoom-routing.module file, we are going to declare a single route that will have two parameters, one for the meeting ID and one for its password.

const routes: Routes = [
{ path: ':meeting/:passcode', component: ZoomComponent }
];

So the full path for the zoom.component would be zoom/:meeting/:passcode which corresponds to the one we had already defined in the meeting.component. Now we can continue with the zoom.service.

For this service, first of all, remove the providedIn:'root' option from the decorator and provide this service only in the zoom.module. We also need to inject the HttpClient in the service constructor (don’t forget to import the HttpClientModule in the app.module) to be able to make a call to our endpoint that generates the signature required by Zoom.

The zoom.service will contain the following methods:

  1. loadZoom: Load Zoom’s own scripts (encapsulated within the iframe)
  2. startMeeting: Starts the process to join a meeting, calling getSignature and initZoomMeeting
  3. getSignature: Obtain the signature required by Zoom by making an HTTP request to the endpoint that we specify
  4. initZoomMeeting: Calls the init method of the SDK. If successful, calls joinMeeting. This method wraps the SDK call in a promise to improve flow control
  5. joinMeeting: Calls the join method of the SDK with the signature, API Key, meeting ID and password, and user information. This method wraps the SDK call in a promise to improve flow control

So, the zoom.service looks like this:

Finally, the zoom.component just gets the meeting ID and passcode from the URL parameters and calls the loadZoom and startMeeting methods of the zoom.service.

With this, we would have everything necessary to test that our app works and verify that it does not have any of the problems that I described at the beginning of this article. Remember that we have this sample project available in this repo, in case you need extra help.

--

--

Alan Acuña

Hi there! I have a degree in biomedical engineering but I’m a self taught full-stack developer. I specialize in Angular & Ionic development.