Search code examples
htmlangulartypescripthtml5-videoangular-directive

Video html play with angular2+ directive using Universal SSR


I am currently trying to implement an Angular directive that automatically mute and play an HTML5 video tag.

I am running this code in Angular 10. The error happens in all browsers that I tested.

Here is my code.

<video appVideoAutoplayMuted [autoplay]="true" preload="metadata" playsinline [controls]="false" [loop]="true" [muted]="true"
       poster="../../../assets/video-mask-spot.png">
  <source src="../../../assets/neon-smash-gameplay-trailer.mp4#t=0.5"
          type="video/mp4">
  <source src="../../../assets/neon-smash-gameplay-trailer.webm#t=0.5"
          type="video/webm">
  <source src="../../../assets/neon-smash-gameplay-trailer.ogv#t=0.5"
          type="video/ogg">
  Your browser does not support the video tag.
</video>

and the directive component looks like

import { Directive, ElementRef, OnInit} from '@angular/core';

@Directive({
  selector: '[appVideoAutoplayMuted]'
})
export class VideoAutoplayMutedDirective implements OnInit {

  constructor(public videoElement: ElementRef) {
  }

  ngOnInit(): void {
    const video: HTMLVideoElement = this.videoElement.nativeElement;
    video.muted = true;
    video.play(); // play is not a function exception
  }
}

When the code reaches video.play(); line it crashes giving me the next error.

ERROR TypeError: video.play is not a function

I debugged the code and the video const is not null and his type is HTMLVideoElement.

Intellisense is also working. I found in mdn that the function exists.

complete error image


Solution

  • I found the solution. The problem was that the code was called on the server-side because I was using Angular Universal. I don't know why but seems like when HTMLVideoElement is in the server doesn't have play(), load() and other methods. I think that this methods are implemented by the browser.

    I solved checking where is the code by running an if statement in the directive.

    import {Directive, ElementRef, Inject, OnInit, PLATFORM_ID} from '@angular/core';
    import {isPlatformBrowser} from '@angular/common';
    
    @Directive({
      selector: '[appVideoAutoplayMuted]'
    })
    export class VideoAutoplayMutedDirective implements OnInit {
    
      constructor(public videoElement: ElementRef, @Inject(PLATFORM_ID) private platformId: any) {
      }
    
      ngOnInit(): void {
        if (isPlatformBrowser(this.platformId)) { // here is the check 
          const video: HTMLVideoElement = this.videoElement.nativeElement;
          video.muted = true;
          video.play();
        }
      }
    }
    

    Please, feels free to expand this answer so we can get a better knowleadge of what is going on.