Skip to content

Commit e9a1219

Browse files
committed
Fix #2
Added support for serverside rendering with Angular Universal Replaced document calls with renderer2 or an injected DOCUMENT
1 parent c59fad8 commit e9a1219

File tree

2 files changed

+52
-18
lines changed

2 files changed

+52
-18
lines changed

projects/pixel/src/lib/pixel.module.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { PixelConfiguration } from './pixel.models';
2-
import { ModuleWithProviders, NgModule } from '@angular/core';
2+
import { Inject, ModuleWithProviders, NgModule, PLATFORM_ID } from '@angular/core';
3+
import { isPlatformBrowser } from '@angular/common';
34
import { PixelService } from './pixel.service';
45

56
@NgModule({
@@ -9,11 +10,14 @@ export class PixelModule {
910

1011
private static config: PixelConfiguration | null = null;
1112

12-
constructor( private pixel: PixelService ) {
13+
constructor(
14+
private pixel: PixelService,
15+
@Inject(PLATFORM_ID) platformId: Object
16+
) {
1317
if (!PixelModule.config) {
1418
throw Error('ngx-pixel not configured correctly. Pass the `pixelId` property to the `forRoot()` function');
1519
}
16-
if (PixelModule.config.enabled) {
20+
if (PixelModule.config.enabled && isPlatformBrowser(platformId)) {
1721
this.pixel.initialize();
1822
}
1923
}

projects/pixel/src/lib/pixel.service.ts

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
import { PixelEventName, PixelConfiguration, PixelEventProperties } from './pixel.models';
2-
import { Inject, Injectable, Optional } from '@angular/core';
2+
import { Inject, Injectable, Optional, PLATFORM_ID, Renderer2, RendererFactory2 } from '@angular/core';
33
import { NavigationEnd, Router } from '@angular/router';
4+
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
45
import { filter } from 'rxjs/operators';
56

6-
declare var fbq: any;
7+
declare const fbq: any;
78

89
@Injectable({
910
providedIn: 'root'
1011
})
1112
export class PixelService {
1213

14+
private doc: Document;
15+
private renderer: Renderer2
16+
1317
constructor(
1418
@Inject('config') private config: PixelConfiguration,
15-
@Optional() private router: Router
19+
@Inject(DOCUMENT) private injectedDocument: any,
20+
@Inject(PLATFORM_ID) private platformId: Object,
21+
@Optional() private router: Router,
22+
private rendererFactory: RendererFactory2
1623
) {
1724

25+
// DOCUMENT cannot be injected directly as Document type, see https://github.com/angular/angular/issues/20351
26+
// It is therefore injected as any and then cast to Document
27+
this.doc = injectedDocument as Document;
28+
this.renderer = rendererFactory.createRenderer(null, null);
29+
1830
if (router) {
1931
// Log page views after router navigation ends
2032
router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(event => {
@@ -33,7 +45,7 @@ export class PixelService {
3345
* - Adds the script to page's head
3446
* - Tracks first page view
3547
*/
36-
initialize(pixelId = this.config.pixelId): void {
48+
initialize(pixelId?: string): void {
3749
if (this.isLoaded()) {
3850
console.warn('Tried to initialize a Pixel instance while another is already active. Please call `remove()` before initializing a new instance.');
3951
return;
@@ -58,8 +70,12 @@ export class PixelService {
5870
track(
5971
eventName: PixelEventName,
6072
properties?: PixelEventProperties
61-
): void {
62-
if(!this.isLoaded()) {
73+
): void {
74+
if (!isPlatformBrowser(this.platformId)) {
75+
return;
76+
}
77+
78+
if (!this.isLoaded()) {
6379
console.warn('Tried to track an event without initializing a Pixel instance. Call `initialize()` first.');
6480
return;
6581
}
@@ -80,7 +96,11 @@ export class PixelService {
8096
* @param properties Optional properties of the event
8197
*/
8298
trackCustom(eventName: string, properties?: object): void {
83-
if(!this.isLoaded()) {
99+
if (!isPlatformBrowser(this.platformId)) {
100+
return;
101+
}
102+
103+
if (!this.isLoaded()) {
84104
console.warn('Tried to track an event without initializing a Pixel instance. Call `initialize()` first.');
85105
return;
86106
}
@@ -97,6 +117,9 @@ export class PixelService {
97117
* @param pixelId The Facebook Pixel ID to use
98118
*/
99119
private addPixelScript(pixelId: string): void {
120+
if (!isPlatformBrowser(this.platformId)) {
121+
return;
122+
}
100123

101124
const pixelCode = `
102125
var pixelCode = function(f,b,e,v,n,t,s)
@@ -110,25 +133,32 @@ export class PixelService {
110133
fbq('init', '${pixelId}');
111134
fbq('track', 'PageView');`;
112135

113-
const scriptElement = document.createElement('script');
114-
scriptElement.setAttribute('id', 'pixel-script');
115-
scriptElement.type = 'text/javascript';
116-
scriptElement.innerHTML = pixelCode;
117-
document.getElementsByTagName('head')[0].appendChild(scriptElement);
136+
137+
const scriptElement = this.renderer.createElement('script');
138+
this.renderer.setAttribute(scriptElement, 'id', 'pixel-script');
139+
this.renderer.setAttribute(scriptElement, 'type', 'text/javascript');
140+
this.renderer.setProperty(scriptElement, 'innerHTML', pixelCode);
141+
this.renderer.appendChild(this.doc.head, scriptElement);
118142
}
119143

120144
/** Remove Facebook Pixel tracking script from the application */
121145
private removePixelScript(): void {
122-
const pixelElement = document.getElementById('pixel-script');
146+
if (!isPlatformBrowser(this.platformId)) {
147+
return;
148+
}
149+
const pixelElement = this.doc.getElementById('pixel-script');
123150
if (pixelElement) {
124151
pixelElement.remove();
125152
}
126153
}
127154

128155
/** Checks if the script element is present */
129156
private isLoaded(): boolean {
130-
const pixelElement = document.getElementById('pixel-script');
131-
return !!pixelElement;
157+
if (isPlatformBrowser(this.platformId)) {
158+
const pixelElement = this.doc.getElementById('pixel-script');
159+
return !!pixelElement;
160+
}
161+
return false;
132162
}
133163

134164
}

0 commit comments

Comments
 (0)