environments.ts
export const environment = {
metaPixelId: '',
};
environment.prod.ts
export const environment = {
metaPixelId: 'XXXXXXXXXXXXXXXX',
};
app.config.ts
import { MetaPixelService } from './services/meta-pixel.service';
export const appConfig: ApplicationConfig = {
providers: [
...
provideAppInitializer(() => inject(MetaPixelService).initialize()),
],
};
index.html
<body itemscope itemtype="http://schema.org/WebPage">
<noscript>
<img
alt=""
height="1"
src="https://www.facebook.com/tr?id=XXXXXXXXXXXXXXXX&ev=PageView&noscript=1"
style="display: none"
width="1"
/>
</noscript>
<app-root></app-root>
</body>
meta-pixel.service.ts
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { DestroyRef, Injectable, PLATFORM_ID, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs';
import { environment } from '../../environments/environment';
type FbqArguments = [command: string, ...args: unknown[]];
type Fbq = {
(...args: FbqArguments): void;
callMethod?: (...args: FbqArguments) => void;
loaded: boolean;
push: Fbq;
queue: FbqArguments[];
version: string;
};
declare global {
interface Window {
_fbq?: Fbq;
fbq?: Fbq;
}
}
@Injectable({
providedIn: 'root',
})
export class MetaPixelService {
private readonly _destroyRef = inject(DestroyRef);
private readonly _document = inject(DOCUMENT);
private readonly _platformId = inject(PLATFORM_ID);
private readonly _router = inject(Router);
private _initialized = false;
private _lastTrackedHref: string | null = null;
public initialize(): void {
if (this._initialized || !isPlatformBrowser(this._platformId) || !environment.metaPixelId) {
return;
}
this._initialized = true;
this._createFbq();
this._loadPixelScript();
window.fbq?.('init', environment.metaPixelId);
this._trackPageView();
this._router.events
.pipe(
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
takeUntilDestroyed(this._destroyRef),
)
.subscribe(() => this._trackPageView());
}
private _createFbq(): void {
if (window.fbq) {
return;
}
const fbq = ((...args: FbqArguments): void => {
if (fbq.callMethod) {
fbq.callMethod(...args);
return;
}
fbq.queue.push(args);
}) as Fbq;
window.fbq = fbq;
if (!window._fbq) {
window._fbq = fbq;
}
fbq.loaded = true;
fbq.push = fbq;
fbq.queue = [];
fbq.version = '2.0';
}
private _loadPixelScript(): void {
if (this._document.getElementById('meta-pixel-script')) {
return;
}
const script = this._document.createElement('script');
script.async = true;
script.id = 'meta-pixel-script';
script.src = 'https://connect.facebook.net/en_US/fbevents.js';
const firstScript = this._document.getElementsByTagName('script')[0];
if (firstScript?.parentNode) {
firstScript.parentNode.insertBefore(script, firstScript);
return;
}
this._document.head.appendChild(script);
}
private _trackPageView(): void {
const currentHref = this._document.location?.href ?? this._router.url;
if (this._lastTrackedHref === currentHref) {
return;
}
this._lastTrackedHref = currentHref;
window.fbq?.('track', 'PageView');
}
}