Skip to content

Commit 56be764

Browse files
authored
Merge pull request #7 from NielsKersic/feature/ssr
Add SSR support
2 parents 8e84035 + e43594a commit 56be764

File tree

5 files changed

+59
-22
lines changed

5 files changed

+59
-22
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ export class HomeComponent {
121121
In situations where the Pixel ID needs to be changed dynamically, this can be done using `initialize()` with the new Pixel ID as an optional argument.
122122
**Notes:**
123123
- A Pixel ID still needs to be provided when importing ***ngx-pixel*** in the module.
124-
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
124+
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
125+
- This approach should **not** be used in combination with serverside rendering (SSR). As the module is initialized on each request, the Pixel script will default to the ID provided in the module.
126+
125127

126128
## Disabling ***ngx-pixel***
127129
Disabling works very similar to *enabling* from within a component and looks like this:
@@ -147,7 +149,7 @@ export class HomeComponent {
147149
---
148150

149151
# What's next?
150-
- [ ] Adding Angular Universal SSR support.
152+
- [X] Adding Angular Universal SSR support.
151153
- [ ] Adding tests.
152154
- [ ] Removing all Facebook scripts on removal, not just the initial script.
153155

projects/pixel/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ export class HomeComponent {
121121
In situations where the Pixel ID needs to be changed dynamically, this can be done using `initialize()` with the new Pixel ID as an optional argument.
122122
**Notes:**
123123
- A Pixel ID still needs to be provided when importing ***ngx-pixel*** in the module.
124-
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
124+
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
125+
- This approach should **not** be used in combination with serverside rendering (SSR). As the module is initialized on each request, the Pixel script will default to the ID provided in the module.
125126

126127
## Disabling ***ngx-pixel***
127128
Disabling works very similar to *enabling* from within a component and looks like this:
@@ -147,7 +148,7 @@ export class HomeComponent {
147148
---
148149

149150
# What's next?
150-
- [ ] Adding Angular Universal SSR support.
151+
- [X] Adding Angular Universal SSR support.
151152
- [ ] Adding tests.
152153
- [ ] Removing all Facebook scripts on removal, not just the initial script.
153154

projects/pixel/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://niels.codes"
88
},
99
"license": "MIT",
10-
"version": "1.0.2",
10+
"version": "1.1.0",
1111
"repository": {
1212
"type": "git",
1313
"url": "https://github.com/NielsKersic/ngx-pixel"

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: 44 additions & 14 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 => {
@@ -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)