3

i have a simple angular application to play audio using JavaScript web Audio Object below is

app.component.ts

export class AppComponent {
  title = 'media player';
  audio;
  currentTime: number;

  constructor() {
    this.audio = new Audio();
    this.audio.src = './Music/demo2.MP3';
    this.audio.play();
    this.currentTime = this.audio.currentTime;
  }
}

and app.component.ts

<h4>{{ currentTime }}</h4>

everything works fine but the view isn't update as the model changes

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Ochui Princewill
  • 148
  • 1
  • 15
  • 1
    Can you not bind to `audio.currentTime` instead? – user184994 Oct 21 '17 at 17:04
  • This is because this line `this.currentTime = this.audio.currentTime;` only runs once when the component is loaded. You need to make sure that line updates every second. Or @user184994 approach could also work. – Prav Oct 21 '17 at 17:10
  • @PraveenM yes i undersand `this.currentTime = this.audio.currentTime;` only run once. please do you know a way to make these work. – Ochui Princewill Oct 21 '17 at 17:13
  • @OchuiPrincewill You can use JavaScript `setInterval` function to update the value every second. – Prav Oct 21 '17 at 17:14

2 Answers2

2

Angular does update binding on browser events for the same Angular uses zonejs which monkey patches several browser events, and it fires detectChanges method to keep binding in a sync.

In this case Angular doesn't update binding, since Audio API event's doesn't monkey patch by zonejs. For such scenarios you have to run change detection manually to update bindings manually. You could use ontimeupdate event handler of audio API.

import { ChangeDetectorRef, Component } from '@angular/core';

export class AppComponent {
  title = 'media player';
  audio;
  currentTime: number;

  constructor(private cd: ChangeDetectorRef ) {
  }
  ngOnInit(){
     this.audio = new Audio();
     this.audio.src = './Music/demo2.MP3';
     this.audio.play();
     //this will make sure to update when time updates.
     this.audio.ontimeupdate = (event) => {
        this.currentTime = this.audio.currentTime;
        this.cd.detectChanges();
     }
  }
}
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
-2

Here is the setInterval approach

export class AppComponent {
  title: string = 'media player';
  audio: Audio;
  currentTime: number;

  constructor() {
    this.audio = new Audio();
    this.audio.src = './Music/demo2.MP3';
    this.audio.play();
    this.currentTime = this.audio.currentTime;
  }
  
   ngOnInit() {
     setInterval(() => {
      this.currentTime = this.audio.currentTime;
    }, 1000);
   }
}

But as the @user184994 mentioned, simply just by using the audio.currentTime on the frontend might be far simpler and effective approach.

<h4>{{ audio.currentTime }}</h4>
Prav
  • 2,785
  • 1
  • 21
  • 30
  • I consider `setInterval` a code smell. It complicates the code and tests, and doesn't seem necessary given when the OP is trying to accomplish. – stealththeninja Oct 21 '17 at 18:47
  • @stealththeninja OP's problem is that `currentTime` not updating constantly when the audio is playing, this is an approach that I would consider. That's why I mentioned of using the `audio.currentTime` itself, as I'm assuming, will get updated as the audio is playing, is far stable and effective. – Prav Oct 21 '17 at 18:58
  • It's preferable to use event listeners over `setInterval`, and media elements have a `timeupdate` event: https://developer.mozilla.org/en-US/docs/Web/Events/timeupdate – stealththeninja Oct 21 '17 at 19:04
  • @stealththeninja yes, I used the same in my answer :) – Pankaj Parkar Oct 24 '17 at 16:07