6
\$\begingroup\$

I just built an audio player in Angular 2 using a player component and a player service. It's all working fine, I just feel like there is a much better way to do this.

Should the audio object be in the service or the component? I'm skeptical because I'm using three different observables and I don't think that is the best way to do it.

player.component.ts:

export class PlayerComponent implements OnInit, OnDestroy {

  // General variables
  private song: Song;
  private currentTime: string;
  private fullTime: string;
  private isPlaying: boolean;
  // Subscription variables
  private songSubscription: any;
  private currentTimeSubscription: any;
  private fullTimeSubscription: any;

  constructor(private _playerService: PlayerService) {
  }

  ngOnInit() {
    this.songSubscription = this._playerService.song.subscribe(data => this.song = data);
    this.currentTimeSubscription = this._playerService.currentTime.subscribe(data => this.currentTime = data);
    this.fullTimeSubscription = this._playerService.fullTime.subscribe(data => this.fullTime = data);
    console.log("Player subscription initialized");
  }

  toggleAudio() {
    this.isPlaying = this._playerService.toggleAudio();
  }

  ngOnDestroy() {
    this.songSubscription.unsubscribe();
    this.currentTimeSubscription.unsubscribe();
    this.fullTimeSubscription.unsubscribe();
    console.log("Player subscription destroyed");
  }

}

player.service.ts:

export class PlayerService {

  private audio: any;
  public song: Subject<Song> = new Subject<Song>();
  public currentTime: Subject<string> = new Subject<string>();
  public fullTime: Subject<string> = new Subject<string>();


  constructor(private _utilityService: UtilityService) {
    this.audio = new Audio();
  }

  setPlayer(song: Song) {
    this.song.next(song);
    this.audio.src = song.audio;
    this.audio.oncanplaythrough = () => {
      this.audio.play();
      this.fullTime.next(
        this._utilityService.getFormatedTime(this.audio.duration)
      );
    };
    this.audio.ontimeupdate = () => {
      this.currentTime.next(
        this._utilityService.getFormatedTime(this.audio.currentTime)
      );
    };
  }

  toggleAudio() {
    if (this.audio.paused) {
      this.audio.play();
    } else {
      this.audio.pause();
    }

    return this.audio.paused;
  }

}

player.component.html:

<ul *ngIf="song" class="player">
  <li class="player-item">
    <a class="player-link" (click)="toggleAudio()">
      <i *ngIf="isPlaying"  class="fa fa-play" aria-hidden="true"></i>
      <i *ngIf="!isPlaying" class="fa fa-pause" aria-hidden="true"></i>
    </a>
  </li>
  <li class="player-item">
    <a class="player-link"><i class="fa fa-volume-up" aria-hidden="true"></i></a>
  </li>
  <li class="player-item">
    <a class="player-link">{{currentTime}}</a>
  </li>
  <li class="player-item">
    <a class="player-link"><i class="fa fa-fast-backward" aria-hidden="true"></i></a>
  </li>
  <li class="player-desc">
    <a class="player-link">{{song.title}} by {{song.artist}}</a>
  </li>
  <li class="player-item">
    <a class="player-link"><i class="fa fa-fast-forward" aria-hidden="true"></i></a>
  </li>
  <li class="player-item">
    <a class="player-link">{{fullTime}}</a>
  </li>
  <li class="player-item">
    <a class="player-link"><i class="fa fa-plus" aria-hidden="true"></i></a>
  </li>
  <li class="player-item">
    <a class="player-link"><i class="fa fa-share" aria-hidden="true"></i></a>
  </li>
</ul>

It's a pretty primitive player right now. I want to make sure I'm implementing it correctly before I add more features.

\$\endgroup\$
  • 2
    \$\begingroup\$ I think it is correct. Im doing the same thing. But im using Event (event emitter) to send Player Service info to other components. \$\endgroup\$ – Paulo Coutinho Nov 11 '16 at 20:44
3
\$\begingroup\$

There is one way I can think of improving this. In angular2, components should only handle view logic. The problem isn't there at the moment but could be there when you add more functionalities. Let's say you want to add a recorder for the song that saves the song from time 0 to time 1. You'd have to add that to this component and keep cluttering it.

I would treat this component as a "master" songPlayer. It operates on a song (i.e. Your song model) so that is okay. It could also operate on the "Player" model. Player has a state. It has its time, knows if it is playing and where the current time is.

Perhaps the best way to structure this would be to start with models: PlayerModel(time, isPlaying, currentTime, song). Song is as it is. What this does is that it creates only one subscription on your playerComponent. To the Player.

I hope I'm not keeping it too abstract with this. It was just an idea.

\$\endgroup\$

protected by Community Apr 14 '18 at 19:41

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.