【问题标题】:Conditional class based on service property in Angular 5Angular 5中基于服务属性的条件类
【发布时间】:2018-02-14 17:03:42
【问题描述】:

我有一个简单的 Angular 5 应用程序,它需要一个连续的音频播放器。它目前由一个列出音频文件的组件、一个包含音频播放器的组件和一个实际播放文件的音频播放器服务组成。

当用户单击其中一个曲目时,audioplayer 服务开始使用 wavesurfer.js 库播放此曲目,该库位于位于 audioplayer 组件内的 #waveform div 中。这由服务内的公共对象wavesurfer 处理。当曲目播放完毕时,此对象会发出一个事件。我在音频播放器模板中有一些 div(按钮和指示器),我想根据曲目是否完成播放来更改类。

我尝试在组件中添加以下代码:

this.audioPlayerService.wavesurfer.on('finish', function () {
   alert('works');
});

但这不起作用。我尝试遵循我在网上找到的其他一些示例,但我似乎无法理解如何在服务和组件之间进行通信。无论如何,我发现的大多数事情都在谈论 Angular 2,所以我不确定它是否不起作用是因为我做错了,还是因为它是不同的版本。如何查看音频播放器组件中的完成事件,或者如何查看公共变量播放的变化?

这是服务中的代码:

import {EventEmitter, Injectable} from '@angular/core';
import * as WaveSurfer from 'wavesurfer';

@Injectable()
export class AudioPlayerService {

  public wavesurfer: WaveSurfer;
  public playing = false;

  constructor() { }

  setTrack(track): void {
      const that = this;

      // create a new instance
      this.wavesurfer = WaveSurfer.create({
          container: '#waveform'
      });

      // fire the play function
      this.wavesurfer.on('ready', function () {
          that.play();
      });

      // load the waveform
      this.wavesurfer.load(track.preview_url);

      this.wavesurfer.on('finished', function () {
          this.playing = false;
      });
  }

  play() {
      this.wavesurfer.play();
      this.playing = true;
  }

}

【问题讨论】:

    标签: angular components


    【解决方案1】:

    你可以通过Rxjs/Subject实现你想要的。

    @Injectable()
    export class AudioPlayerService {
    
        public wavesurfer: WaveSurfer;
        _playing: Subject<boolean> = new Subject(false);
        playing$: Observable<boolean> = _playing.asObservable();
    
        constructor() { }
    
        setTrack(track): void {
            const that = this;
    
            // create a new instance
            this.wavesurfer = WaveSurfer.create({
                container: '#waveform'
            });
    
            // fire the play function
            this.wavesurfer.on('ready', function () {
                that.play();
            });
    
            // load the waveform
            this.wavesurfer.load(track.preview_url);
    
            this.wavesurfer.on('finished', function () {
                this.playing.next(false);
            });
        }
    
        play() {
            this.wavesurfer.play();
            this.playing.next(true);
       }
    }
    

    在组件文件中,订阅playing$

    @Component({})
    export class SomeComponent implements OnInit{
        constructor(private audioPlayerService : AudioPlayerService ) {}
    
        ngOnInit() {
            this.audioPlayerService.playing$.subscribe(next => {
                 if (next === false) {
                     // playing is false
                 } else {
                     // playing is true
                 }
            })
        }
    }
    

    【讨论】:

    • 感谢您的回复!我正在使用 phpstorm,当我添加 _playing: Subject&lt;boolean&gt; = new Subject(false); 时,我收到错误“预期 0 个参数,但得到 1”。无论如何,页面加载没有错误,无论我是否放(假)都不会改变行为。所以我认为这是phpstorm中的一个错误。除此之外,它似乎工作。我想知道,这不是很耗费资源吗?由于组件现在从创建的那一刻起就开始永远监听 play$ 的变化。还是没关系?
    • 你的 rxjs 版本是多少?
    • 另外,对于您的问题,您可以(并且应该)在组件被销毁时取消订阅(使用ngOnDestroy)。有多少组件可以收听?
    • 抱歉我的回复晚了。我的 rxjs 版本是 5.5.6。只有一个组件在监听它,但它需要在播放曲目时进行监听。 audioplayer 服务永远不会被破坏,因为它是一个连续播放器。如果播放器组件永远在监听完成事件,这是否重要(资源方面)?否则我必须想办法在曲目开始播放时启动订阅。
    【解决方案2】:

    在阅读了您的问题后,我想出了一个想法,即您需要 component 来播放曲目,因此您可以毫无问题地在组件内使用 wavesurfer 的所有功能,这将很容易实现和理解。

    但是,如果您坚持为此提供服务。我相信Sevice 应该是无国籍的;使用像playing 这样的公共变量不是一个好主意。另一方面,您的服务的使用者不应访问您声明的wavesurfer

    通常,您的服务可以引发通知服务内部发生的事情的事件。很容易实现。

    import {EventEmitter, Injectable} from '@angular/core';
    import * as WaveSurfer from 'wavesurfer';
    
    @Injectable()
    export class AudioPlayerService {
    
      private _wavesurfer: WaveSurfer;
      // public playing = false;
      public finished: EventEmitter<any> = new EventEmitter();
      public started: EventEmitter<any> = new EventEmitter();
    
      constructor() { }
    
      setTrack(track, container): void {
        const that = this;
    
        // create a new instance
        this.wavesurfer = WaveSurfer.create({
          container: container
        });
    
        // fire the play function
        this.wavesurfer.on('ready', function () {
          that.play();
          that.started.emit();
        });
    
        // load the waveform
        this.wavesurfer.load(track.preview_url);
    
        this.wavesurfer.on('finished', function () {
          that.finished.emit();
        });
      }
    
      play() {
        this.wavesurfer.play();
        //this.playing = true;
        this.started.emit();
      }
    

    我不得不提一下,为服务中的每个重要操作声明一个特定的事件,这使得它易于理解和使用。

    【讨论】:

    • 感谢您的回复。当我阅读您的解决方案时,它似乎与 Bunyamin 的解决方案相似。在这两种方法之间做出选择时,我是否需要考虑一个重要的根本区别?另外,我认为将所有与加载内容相关的代码放在服务中以及与在组件中显示相关的代码是推荐的做法(如果不正确,请原谅我,我是 Angular 的新手)。或者您会说该服务当前正在做什么属于显示内容的类别(因为它没有进行 http 调用或其他任何事情)。
    • 那么,wavesurfer 是什么?基于其网站上的描述; wavesurfer.js is a customizable audio waveform visualization, built on top of Web Audio API and HTML5 Canvas.;在我看来,serviceHTML5。因此,它的所有功能都与visualization 相关,并且服务已由wavesurfer 提供。我关心的是如何设计一个组件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多