0

I need to implement a facebook pixel in an angular proyect. I have the pixel in my index.html from src/index.html

<!doctype html>
<html lang="en">
  <head>
  .....
  </head>

<body>
  <app-root></app-root>
  <!-- Facebook Pixel Code -->
  <script>
    !function(f,b,e,v,n,t,s)
    {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
    n.callMethod.apply(n,arguments):n.queue.push(arguments)};
    if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
    n.queue=[];t=b.createElement(e);t.async=!0;
    t.src=v;s=b.getElementsByTagName(e)[0];
    s.parentNode.insertBefore(t,s)}(window, document,'script',
    'https://connect.facebook.net/en_US/fbevents.js');
    fbq('init', 'XXXXXXXXXXXXX');
    fbq('track', 'PageView');
  </script>
  <noscript><img height="1" width="1" style="display:none"
  src="https://www.facebook.com/tr?id=XXXXXXXXXXXXX&ev=PageView&noscript=1"
  /></noscript>
  <!-- End Facebook Pixel Code -->
  <script>
  </script>
</body>
</html>

That's how it was "implemented" but it doesn't work, at least is what the facebook panel says.

What's is the right way to do this in a SPA made with Angular?

akko
  • 203
  • 4
  • 9

1 Answers1

1

I had the same problem and I come up with the following solution. It will work only for apps using Router. Hope it helps:

  1. In your app folder create a folder for the pixel service (e.g. facebook-pixel-service), then create the file for the service code (e.g. facebook-pixel.service.ts) and copy the code bellow that works like following:

    • The first time the app loads the root component of your app it will execute attaching the facebook script to the root component html. It will also call init and fire a PageView event for the current page (typically it will be path "/" on Router).
    • On all other executions that should correspond to the other paths/pages of your app, it will just fire the PageView event.

      import { Injectable } from '@angular/core';
      @Injectable({
        providedIn: 'root'
      })
      export class FacebookPixelService {
      
        private loadOk:boolean = false;
      
        constructor() { }
      
        load() {
          if (!this.loadOk) {
            (function (f: any, b, e, v, n, t, s) {
                if (f.fbq) return; n = f.fbq = function () {
                    n.callMethod ?
                        n.callMethod.apply(n, arguments) : n.queue.push(arguments)
                }; if (!f._fbq) f._fbq = n;
                n.push = n; n.loaded = !0; n.version = '2.0'; n.queue = []; t = b.createElement(e); t.async = !0;
                t.src = v; s = b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t, s)
            })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
            (window as any).fbq.disablePushState = true; //not recommended, but can be done
            (window as any).fbq('init', '381817369337156');
            (window as any).fbq('track', 'PageView');
            this.loadOk = true;
            console.log('Facebook pixel init run!')
          }
          else {
            (window as any).fbq('track', 'PageView');
            console.log('Facebook PageView event fired!')
          }
        }
      }
      
  2. On the same folder of the service create another file (e.g. facebook-pixel.provider.ts) for the provider that will inject the service on app.module. The code bellow will just create a provider that subscribe our pixel service to be called every time a Router event is fired and this event is a NavigationEnd which means it was a successful change on the path/page.

    import { Provider, APP_BOOTSTRAP_LISTENER, ComponentRef } from '@angular/core';
    import { Router, NavigationEnd } from '@angular/router';
    import { FacebookPixelService } from './facebook-pixel.service'
    
    export const FACEBOOK_PIXEL_PROVIDER: Provider = {
      provide: APP_BOOTSTRAP_LISTENER,
      multi: true,
      useFactory: FacebookPixelRouterInitializer,
      deps: [
        FacebookPixelService,
        Router
      ]
    };
    
    export function FacebookPixelRouterInitializer(
      $fpService: FacebookPixelService,
      $router: Router
    ) {
      return async (c: ComponentRef<any>) => {
        $router
          .events
          .subscribe(event => {
            if (event instanceof NavigationEnd) {
              console.log(`Navigated to URL: ${event.url}`);
              $fpService.load();
            }
          });
      };
    }
    
  3. Now, just inform the provider on the @NgModule directive on the app.moudle file. The framework will do the rest and make sure it will subscribe our provider to the Router and call it when appropriate. The snippet below shows how to inform the provider (search for FACEBOOK_PIXEL_PROVIDER):

    ...
    import { RouterModule, Routes } from '@angular/router';
    ...
    import { AppComponent } from './app.component';
    ...
    import { FACEBOOK_PIXEL_PROVIDER } from './facebook-pixel-service/facebook-pixel.provider'
    
    @NgModule({
      declarations: [
        AppComponent,
        ...
      ],
      imports: [
        ...
        RouterModule.forRoot([
          { path: '', Component: TheRootPath},
          { path: 'path1', Component: Path1},
          { path: 'path2', Component: Path2},
          { path: '**', component: PageNotFoundComponent },
        ]),
      ],
      providers: [
        FACEBOOK_PIXEL_PROVIDER
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
mvaz73
  • 11
  • 1
  • Hi, this solution will not work with nested module structure, where parent module will lazy load another module... should we need to add provider into sub module too ? – mayur kukadiya Nov 30 '20 at 11:58