Facebook Comments Widget lazy loaded with Angular 2

1.8k views Asked by At

In short: The Facebook Comments Widget only gets loaded the first time the page is accessed, after that it does not load anymore. I need a way for the script and anything else to be executed again just like it was executed the first time the page was accessed.

Explanation: I want to display the Facebook Comment Widget at the bottom of a single blog post, this works, but when I come back later to the blog post or any other blog post for that matter the FB Widget does not load/show. The guide that I'm following is Facebook Developers Comment Widget

What I have:

Extract from routing:

const routes: Routes = [
  {
    path: '',
    component: BlogComponent,
    children: [
      {
        path: ':slug',
        pathMatch: 'full',
        loadChildren: () => PostModule
      },
      {
        path: '',
        loadChildren: () => PostsModule
      }
    ]
  }
];

Extract from post.component.html (OLD):

<div [innerHtml]="post.html"></div>
<div id="fb-root"></div>
<div class="fb-comments" [attr.data-href]="currentURL" data-numposts="5"></div>

Extract from post.component.ts (OLD):

...
export class PostComponent implements AfterViewInit{
  ...
  ngAfterViewInit(){
    this.initFBComments();
  }

  initFBComments(){
    (function(d, s, id) {
      var js, fjs = d.getElementsByTagName(s)[0];
      if (d.getElementById(id)) return;
      js = d.createElement(s); js.id = id;
      js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.8";
      fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));        
  }
}

The problem: As I mentioned this works, but only the first time the page is accessed. When I return the FB-SDK that was downloaded the first time already exists and nothing is executed (since if (d.getElementById(id)) return; is executed)


Changes I made: I have tried a lot of different things from using ngOnInit, ngAfterViewChecked even a function called on a click event. The problem is nothing is loaded because there is already an SDK downloaded (because of the lazy loading).

If I change the code a bit so that the html is added dynamically and run some code from the SDK I can get it to fetch the data again, but still it is not displayed due to it having set the height of the widget to 0 and also it has the widget fb_hide_iframes class.

How the files are now:

Extract from post.component.html (NEW):

<div [innerHtml]="post.html"></div>
<div #fbComments></div>

Extract from post.component.ts (NEW):

...
declare var window: any;
export class PostComponent implements AfterViewInit{
  @ViewChild('fbComments') elementRef: ElementRef;
  ...
  ngAfterViewInit(){
    this.initFBComments();
  }

  initFBComments(){
    //Add the html tags dynamically.
    this.renderer.setElementProperty(this.elementRef.nativeElement, 'innerHTML', '<div id="fb-root"></div><div class="fb-comments" data-href="'+this.currentURL+'" data-numposts="5"></div>');

    (function(d, s, id) {
      var js, fjs = d.getElementsByTagName(s)[0];
      if (d.getElementById(id)){
        window.FB.XFBML.parse(); //Instead of returning, lets call parse()
      }
      js = d.createElement(s); js.id = id;
      js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.8";
      fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));        
  }
}

Results:

When the page is accessed the 1st time:

<div _ngcontent-bbg-8="" id="fb-root" class=" fb_reset">
  <div style="position: absolute; top: -10000px; height: 0px; width: 0px;">...</div>
  <div style="position: absolute; top: -10000px; height: 0px; width: 0px;">...<div>
</div>
<div _ngcontent-bbg-8="">
  <div class="fb-comments fb_iframe_widget" data-href="localhost/blog/testing" data-numposts="5" fb-xfbml-state="rendered">
    <span style="height: 944px; width: 550px;"><iframe ...></iframe></span>
  </div>
</div>

When the page is accessed the 2nd time:

<div _ngcontent-bbg-8="" id="fb-root"></div>
<div _ngcontent-bbg-8="">
  <div class="fb-comments fb_iframe_widget_loader fb_iframe_widget fb_hide_iframes" data-href="localhost/blog/testing" data-numposts="5" fb-xfbml-state="rendered">
    <span style="height: 100px; width: 550px;"><iframe ...</iframe></span>
  </div>
</div>
2

There are 2 answers

1
Hendri Potgieter On

So the first line in the SDK that is downloaded is: try {window.FB|| ...

Thus by adding:

ngOnDestroy() {
  delete window.FB;
}

Solved everything. Facebook's SDK now executes everything on every load as if it did not exist previously

0
aGuyInLasVegas On

As a follow-up to @Hendri's answer, I just wanted to mention in addition to

delete window.FB;

I also had to delete the 'facebook-jssdk' element as well or it would not reload. So compete code:

ngOnDestroy() {
  (function (d, s, id) {
    let js, fjs = d.getElementsByTagName(s)[0];
    let jssdk = d.getElementById(id);
    if (jssdk)
      fjs.parentNode.removeChild(jssdk);
    delete window.FB;
  })(document, 'script', 'facebook-jssdk');
}