How to communicate with Angular components outside of Angular

javascript popular
Communicating with Angular components from the outside.

I recently had to figure out how to interact with Angular components from outside of Angular, from plain JavaScript rendered to a page. In my case, I was opening a modal dialog written in Angular from a traditional server-side rendered page.

I imagine I am not the only one who needs to mix Angular code with other code on the page, so I want to share a code that will allow you to do that easily. I based it on a publish-subscribe pattern to separate the code in a nice and clean way.

The primary use-cases are:

I wrote it as an Angular service, named GlobalPubSub. This service must be part of the Angular module that you load on the page. It will then attach functions on the global window object that you can call outside of Angular.

Basic usage:

A few notes on implementation:

Here it is, let me know what you think!

import {Injectable, NgZone} from "@angular/core";

/**
* Service that allows Angular components to receive and fire
* events from outside
*
* Usage from outside of Angular:
* window.fireAngularEvent('sampleEventName', args)
* window.subscribeToAngularEvent('sampleEventName', fn)
*
* Usage from Angular component:
* globalPubSub.fireEvent('sampleEventName', args)
* globalPubSub.subscribe('sampleEventName', fn)
*/

@Injectable()
export class GlobalPubSub {

allowedEvents = [
"sampleEventName",
"sampleEventName2"
];

private subscriptions : {[key:string]:Function[];} = {};

constructor(private zone: NgZone) {
this.allowedEvents.forEach((eventName) => {
this.subscriptions[eventName] = []
});

window['fireAngularEvent'] = (eventName, args) => {
if (!this.subscriptions[eventName]) {
throw new Error('Event has to be defined in the event list.')
}

zone.run(() => {
this.fireEvent(eventName, args);
});
};

window['subscribeToAngularEvent'] = (eventName, fn) => {
this.subscribe(eventName, fn);
};
}

subscribe(eventName: string, fn: Function) {
if (!this.subscriptions[eventName]) {
throw new Error('Event has to be defined in the event list.');
}

this.subscriptions[eventName].push(fn);
}

fireEvent(eventName: string, args) {
if (!this.subscriptions[eventName]) {
throw new Error('Event has to be defined in the event list.');
}

this.subscriptions[eventName].forEach((fn) => {
fn.apply(null, args);
});
}
}

Last updated on 8.3.2022.