URL sanitization is causing refresh of the embedded YouTube video

I have a problem with my AngularJS 2 app (I am using RC5 version of AngularJS 2). It seems that a sanitized URL is triggering change detection which then updates the div below despite not having any changes in my component state.

From user point of view this manifests itself as reload of the video while it is playing.

So in my component view I have:

<div *ngIf="isVideo(item)">
   <iframe [src]="getTrustedYouTubeUrl(item)" scrolling="no" frameborder="0" allowfullscreen></iframe>          
</div>

The implementation of the function above in the component code is:

getTrustedYouTubeUrl(linkedVideo:LinkedVideoModel) {
    return this.sanitizer.bypassSecurityTrustResourceUrl(linkedVideo.video.url);
}    

In debugger I see that the div gets refreshed quite often, by something triggered in AngularJS 2 framework.

The issue goes away, if I replace the HTML snippet above with a hard-coded URL:

<div *ngIf="isVideo(item)">
   <iframe src="<hardcoded youtube url here>" scrolling="no" frameborder="0" allowfullscreen></iframe>          
</div> 

So I am suspecting that URL sanitization is causing it.

Can anyone point me in the right direction? A working example of embedded YouTube videos that have their URL bound to a property on the component maybe?


Solution 1:

Figured it out.

For anyone interested. The problem was the use of this function in my html

   [src]="getTrustedYouTubeUrl(item)"

The reload side effect was gone once I changed the code to calculate the safe url when the data is loaded in my service and changed the iframe binding to this

    <iframe [src]="item.video.safeUrl" scrolling="no" frameborder="0" allowfullscreen></iframe>    

Note thatr I am now binding to a property.

Solution 2:

I would try to use it with pure pipe

Angular executes a pure pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (String, Number, Boolean, Symbol) or a changed object reference (Date, Array, Function, Object).

Pipe might look like (RC.6 - DomSanitizer becomes instead of DomSanitizationService):

@Pipe({ name: 'safe' })
export class SafePipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}
  transform(url) {
    return this.sanitizer.bypassSecurityTrustResourceUrl(url);
  }
}

And use it as the following:

<iframe [src]="url | safe" frameborder="0" allowfullscreen></iframe> 

Plunker Example (try to push button)

Solution 3:

Combined the previous answers to get it working this way:

component.ts (only the relevant parts)

import { DomSanitizer } from '@angular/platform-browser';

constructor( 
  private sanitizer: DomSanitizer   
) {
  this.sanitizer = sanitizer;
}

ngOnInit() {
 this.getTrustedUrl('http://someUrl');
}

getTrustedUrl(url:any){ 
 this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
}

template.html

<iframe      
  [src]='this.safeUrl'>
</iframe>

Solution 4:

You can keep your original solution and just use changeDetection: ChangeDetectionStrategy.OnPush

<div *ngIf="isVideo(item)">
   <iframe [src]="getTrustedYouTubeUrl(item)" scrolling="no" frameborder="0" allowfullscreen></iframe>          
</div>

Solution 5:

<iframe #ytplayer  width="100%" height="235" frameborder="0" src="">
</iframe>    

@ViewChild('ytplayer', null) ytPlayer: ElementRef;

ngOnInit() {
  this.ytPlayer.nativeElement.src = this.act.iframe;
}

Reloading happens because of using sanitizer.bypassSecurityTrustResourceUrl. So we should not use it. I think it is more comfortable. Thanks.