Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mobileBridge to hybrid apps #17

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,15 @@ export class HomeComponent {

}
```

## Enabling mobileBridge to Hybrid Mobile App Events ***ngx-pixel***
It sends the events to app container following this link: https://developers.facebook.com/docs/app-events/hybrid-app-events/.
When adding ***ngx-pixel*** to `app.module.ts`, add the parameter `appId: 'YOUR_PIXEL_APP_ID'`.
```TypeScript
imports: [
BrowserModule,
PixelModule.forRoot({ enabled: true, pixelId: 'YOUR_PIXEL_ID', appId: 'YOUR_PIXEL_APP_ID'})
],
```
---

# Important notes
Expand All @@ -150,7 +158,7 @@ export class HomeComponent {

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

---
Expand Down
12 changes: 11 additions & 1 deletion projects/pixel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ export class HomeComponent {
}
```

## Enabling mobileBridge to Hybrid Mobile App Events ***ngx-pixel***
It sends the events to app container following this link: https://developers.facebook.com/docs/app-events/hybrid-app-events/.
When adding ***ngx-pixel*** to `app.module.ts`, add the parameter `appId: 'YOUR_PIXEL_APP_ID'`.
```TypeScript
imports: [
BrowserModule,
PixelModule.forRoot({ enabled: true, pixelId: 'YOUR_PIXEL_ID', appId: 'YOUR_PIXEL_APP_ID'})
],
```

---

# Important notes
Expand All @@ -149,7 +159,7 @@ export class HomeComponent {

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

---
Expand Down
2 changes: 2 additions & 0 deletions projects/pixel/src/lib/pixel.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export interface PixelConfiguration {
enabled?: boolean;
/** Your Facebook Pixel ID */
pixelId: string;
/** Your Facebook APP ID, only is required to mobileBridge */
appId?: string;
}

export interface PixelEventProperties {
Expand Down
2 changes: 1 addition & 1 deletion projects/pixel/src/lib/pixel.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class PixelModule {

return {
ngModule: PixelModule,
providers: [PixelService, { provide: 'config', useValue: config }]
providers: [PixelService, { provide: 'config', useValue: config }],
};
}

Expand Down
27 changes: 26 additions & 1 deletion projects/pixel/src/lib/pixel.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
import { TestBed } from '@angular/core/testing';

import { PixelService } from './pixel.service';
import { PLATFORM_ID } from '@angular/core';
import { PixelEventName, PixelEventProperties } from './pixel.models';


describe('PixelService', () => {
let service: PixelService;

beforeEach(() => {
TestBed.configureTestingModule({});
TestBed.configureTestingModule({
providers: [
{ provide: 'config', useValue: { enabled: true, pixelId: '123000' } },
],
});
service = TestBed.inject(PixelService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});

it('should console a warning when the script was already loaded', () => {
service.initialize('12000');
spyOn(console, 'warn');
service.initialize('12000');
expect(console.warn).toHaveBeenCalledWith(
'Tried to initialize a Pixel instance while another is already active. Please call `remove()` before initializing a new instance.'
);
});

it('should console a warning when the script wasnt loaded', () => {
service.remove();
spyOn(console, 'warn');
service.track('AddToCart');
expect(console.warn).toHaveBeenCalledWith(
'Tried to track an event without initializing a Pixel instance. Call `initialize()` first.'
);
});
});
72 changes: 45 additions & 27 deletions projects/pixel/src/lib/pixel.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
import { PixelEventName, PixelConfiguration, PixelEventProperties } from './pixel.models';
import { Inject, Injectable, Optional, PLATFORM_ID, Renderer2, RendererFactory2 } from '@angular/core';
import {
PixelEventName,
PixelConfiguration,
PixelEventProperties,
} from './pixel.models';
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 const fbq: any;
declare const fbq: (
type: 'track' | 'trackCustom',
eventName: PixelEventName | string,
properties?: PixelEventProperties
) => void;

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

private doc: Document;
private renderer: Renderer2
private renderer: Renderer2;

constructor(
@Inject('config') private config: PixelConfiguration,
Expand All @@ -21,23 +35,21 @@ export class PixelService {
@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 => {

if (this.isLoaded()) {
this.track('PageView');
}

});
router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe((event) => {
if (this.isLoaded()) {
this.track('PageView');
}
});
}

}

/**
Expand All @@ -47,11 +59,13 @@ export class PixelService {
*/
initialize(pixelId = this.config.pixelId): void {
if (this.isLoaded()) {
console.warn('Tried to initialize a Pixel instance while another is already active. Please call `remove()` before initializing a new instance.');
console.warn(
'Tried to initialize a Pixel instance while another is already active. Please call `remove()` before initializing a new instance.'
);
return;
}
this.config.enabled = true;
this.addPixelScript(pixelId);
this.addPixelScript(pixelId, this.config.appId);
}

/** Remove the Pixel tracking script */
Expand All @@ -67,16 +81,15 @@ export class PixelService {
* @param eventName The name of the event that is being tracked
* @param properties Optional properties of the event
*/
track(
eventName: PixelEventName,
properties?: PixelEventProperties
): void {
track(eventName: PixelEventName, properties?: PixelEventProperties): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}

if (!this.isLoaded()) {
console.warn('Tried to track an event without initializing a Pixel instance. Call `initialize()` first.');
console.warn(
'Tried to track an event without initializing a Pixel instance. Call `initialize()` first.'
);
return;
}

Expand All @@ -85,7 +98,6 @@ export class PixelService {
} else {
fbq('track', eventName);
}

}

/**
Expand All @@ -101,7 +113,9 @@ export class PixelService {
}

if (!this.isLoaded()) {
console.warn('Tried to track an event without initializing a Pixel instance. Call `initialize()` first.');
console.warn(
'Tried to track an event without initializing a Pixel instance. Call `initialize()` first.'
);
return;
}

Expand All @@ -115,12 +129,17 @@ export class PixelService {
/**
* Adds the Facebook Pixel tracking script to the application
* @param pixelId The Facebook Pixel ID to use
* @param appId The Facebook Pixel APP ID to use
*/
private addPixelScript(pixelId: string): void {
private addPixelScript(pixelId: string, appId?: string): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}

const scriptMobileBridge = appId
? `fbq('set', 'mobileBridge', '${pixelId}', '${appId}')`
: '';

const pixelCode = `
var pixelCode = function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
Expand All @@ -131,9 +150,9 @@ export class PixelService {
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '${pixelId}');
${scriptMobileBridge}
fbq('track', 'PageView');`;


const scriptElement = this.renderer.createElement('script');
this.renderer.setAttribute(scriptElement, 'id', 'pixel-script');
this.renderer.setAttribute(scriptElement, 'type', 'text/javascript');
Expand All @@ -160,5 +179,4 @@ export class PixelService {
}
return false;
}

}
10 changes: 2 additions & 8 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,9 @@
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
],
"lib": ["es2018", "dom"],
"paths": {
"pixel": [
"dist/pixel/pixel",
"dist/pixel"
]
"pixel": ["dist/pixel/pixel", "dist/pixel"]
}
},
"angularCompilerOptions": {
Expand Down