Skip to content

Commit

Permalink
Merge pull request #7 from NielsKersic/feature/ssr
Browse files Browse the repository at this point in the history
Add SSR support
  • Loading branch information
NielsCodes authored May 27, 2021
2 parents 8e84035 + e43594a commit 56be764
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 22 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ export class HomeComponent {
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.
**Notes:**
- A Pixel ID still needs to be provided when importing ***ngx-pixel*** in the module.
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
- 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.


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

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

Expand Down
5 changes: 3 additions & 2 deletions projects/pixel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ export class HomeComponent {
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.
**Notes:**
- A Pixel ID still needs to be provided when importing ***ngx-pixel*** in the module.
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
- The previous instance should be removed with `remove()` before initializing a new Pixel ID.
- 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.

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

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

Expand Down
2 changes: 1 addition & 1 deletion projects/pixel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://niels.codes"
},
"license": "MIT",
"version": "1.0.2",
"version": "1.1.0",
"repository": {
"type": "git",
"url": "https://github.com/NielsKersic/ngx-pixel"
Expand Down
10 changes: 7 additions & 3 deletions projects/pixel/src/lib/pixel.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PixelConfiguration } from './pixel.models';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { Inject, ModuleWithProviders, NgModule, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { PixelService } from './pixel.service';

@NgModule({
Expand All @@ -9,11 +10,14 @@ export class PixelModule {

private static config: PixelConfiguration | null = null;

constructor( private pixel: PixelService ) {
constructor(
private pixel: PixelService,
@Inject(PLATFORM_ID) platformId: Object
) {
if (!PixelModule.config) {
throw Error('ngx-pixel not configured correctly. Pass the `pixelId` property to the `forRoot()` function');
}
if (PixelModule.config.enabled) {
if (PixelModule.config.enabled && isPlatformBrowser(platformId)) {
this.pixel.initialize();
}
}
Expand Down
58 changes: 44 additions & 14 deletions projects/pixel/src/lib/pixel.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import { PixelEventName, PixelConfiguration, PixelEventProperties } from './pixel.models';
import { Inject, Injectable, Optional } from '@angular/core';
import { Inject, Injectable, Optional, PLATFORM_ID, Renderer2, RendererFactory2 } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { filter } from 'rxjs/operators';

declare var fbq: any;
declare const fbq: any;

@Injectable({
providedIn: 'root'
})
export class PixelService {

private doc: Document;
private renderer: Renderer2

constructor(
@Inject('config') private config: PixelConfiguration,
@Optional() private router: Router
@Inject(DOCUMENT) private injectedDocument: any,
@Inject(PLATFORM_ID) private platformId: Object,
@Optional() private router: Router,
private rendererFactory: RendererFactory2
) {

// DOCUMENT cannot be injected directly as Document type, see https://github.com/angular/angular/issues/20351
// It is therefore injected as any and then cast to Document
this.doc = injectedDocument as Document;
this.renderer = rendererFactory.createRenderer(null, null);

if (router) {
// Log page views after router navigation ends
router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(event => {
Expand Down Expand Up @@ -58,8 +70,12 @@ export class PixelService {
track(
eventName: PixelEventName,
properties?: PixelEventProperties
): void {
if(!this.isLoaded()) {
): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}

if (!this.isLoaded()) {
console.warn('Tried to track an event without initializing a Pixel instance. Call `initialize()` first.');
return;
}
Expand All @@ -80,7 +96,11 @@ export class PixelService {
* @param properties Optional properties of the event
*/
trackCustom(eventName: string, properties?: object): void {
if(!this.isLoaded()) {
if (!isPlatformBrowser(this.platformId)) {
return;
}

if (!this.isLoaded()) {
console.warn('Tried to track an event without initializing a Pixel instance. Call `initialize()` first.');
return;
}
Expand All @@ -97,6 +117,9 @@ export class PixelService {
* @param pixelId The Facebook Pixel ID to use
*/
private addPixelScript(pixelId: string): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}

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

const scriptElement = document.createElement('script');
scriptElement.setAttribute('id', 'pixel-script');
scriptElement.type = 'text/javascript';
scriptElement.innerHTML = pixelCode;
document.getElementsByTagName('head')[0].appendChild(scriptElement);

const scriptElement = this.renderer.createElement('script');
this.renderer.setAttribute(scriptElement, 'id', 'pixel-script');
this.renderer.setAttribute(scriptElement, 'type', 'text/javascript');
this.renderer.setProperty(scriptElement, 'innerHTML', pixelCode);
this.renderer.appendChild(this.doc.head, scriptElement);
}

/** Remove Facebook Pixel tracking script from the application */
private removePixelScript(): void {
const pixelElement = document.getElementById('pixel-script');
if (!isPlatformBrowser(this.platformId)) {
return;
}
const pixelElement = this.doc.getElementById('pixel-script');
if (pixelElement) {
pixelElement.remove();
}
}

/** Checks if the script element is present */
private isLoaded(): boolean {
const pixelElement = document.getElementById('pixel-script');
return !!pixelElement;
if (isPlatformBrowser(this.platformId)) {
const pixelElement = this.doc.getElementById('pixel-script');
return !!pixelElement;
}
return false;
}

}

0 comments on commit 56be764

Please sign in to comment.