创建绑定到现有函数的 Observable

Creating an Observable that binds to an existing function

我一直在尝试将 ng2-file-upload 包中的 onCompleteItem 数组函数绑定到我可以订阅的 RxJS Observable 方法。

这里是我正在谈论的功能:

onCompleteItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any;

..在使用中几乎与相同:

onCompleteItem(item: any, response: any, status: any, headers: any)

这是来自 MyComponent 的相关部分,目前正在运行:

imageArray: Image[] = [];

uploader:FileUploader = new FileUploader({
  url: '/api/FileUpload'
});

ngOnInit() {

  this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
    const data = JSON.parse(response);
    data.forEach(x => {
      console.log(x);
      this.imageArray.push(x);
    }
    this.uploader.removeFromQueue(item);
  };

}

我试图通过使用 RxJS bindCallback 函数将其绑定到 Observable 中:

bindCallback(this.uploader.onCompleteItem, {item: any, response: any, status: any, headers: any})().pipe(
  tap((item, response) => {
    const data = JSON.parse(response);
    this.uploader.removeFromQueue(item);
    const images = data.map(x => console.log(x) );
    this.imageArray.push(...images);
  })
).subscribe();

这行不通,因为有一些 type/syntax 错误。有没有人对如何将其重写为绑定的 Observable 函数有什么好的建议?

bindCallback 包装了一个函数,该函数在订阅可观察对象时调用,例如getJSON(url, callback)。您正试图通过传递 callback 参数来使用它。

相反,您可以尝试 fromEventPattern 函数,它允许您通过其两个参数指定如何注册事件处理程序,以及如何取消注册。

const {fromEventPattern} = rxjs;

const uploader = {
  upload() {
    setTimeout(() => {
      if (this.onCompleteItem) this.onCompleteItem('arg 1', 'arg 2');
    }, 1000);
  }
};

const source = fromEventPattern(handler => uploader.onCompleteItem = handler, _ => uploader.onCompleteItem = null);

source.subscribe(([arg1, arg2]) => console.log(arg1, arg2));
uploader.upload();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.4/rxjs.umd.js"></script>

这是一次真正的旅程..但我希望这个答案能给一些用户带来新的见解。

这里有一些我最初采用的有效解决方案,列出的最后一个解决方案是我最终实施的。

初步解:

const obs = new Observable<{ item: FileItem, response: string }>(sub => {
  this.uploader.onCompleteItem = (item: FileItem, response: string) => sub.next({ item, response });
  return () => this.uploader.onCompleteItem = undefined;
});

bindCallback((x: (item: FileItem, response: string) => any) => this.uploader.onCompleteItem = x)()
  .pipe(
    takeUntil(this.onDestroySubj),
    tap(([item, response]) => {
      const data = JSON.parse(response);
      this.imageArray.push(...data );
      this.uploader.removeFromQueue(item);
    })
  ).subscribe();

上述解决方案的唯一问题是 bindCallBack 只触发一次。

更完善的解决方案:

Observable.create((sub: Subscriber<[FileItem, string]>) => {
  this.uploader.onCompleteItem = (item: FileItem, response: string) => sub.next([item, response]);
  return () => this.uploader.onCompleteItem = undefined;
}).pipe(
  takeUntil(this.onDestroySubj),
  tap(([item, response]) => {
    const data = JSON.parse(response);
    this.imageArray.push(...data );
    this.uploader.removeFromQueue(item);
  })
).subscribe();

这让我开始思考。我考虑过使用 fromEvent,因为它订阅了 jQuery 操作和浏览器事件,但除非编写了包装器,否则不会。这就是导致我使用 fromEventPattern:

的最终解决方案的原因

最终解决方案:(完整代码)

ngOnInit() {
  fromEventPattern<Parameters<FileUploader['onCompleteItem']>>(x => this.uploader.onCompleteItem = x).pipe(
    takeUntil(this.onDestroySubj),
    tap(([item, response]) => {
      this.imageArray = this.imagesSubject.getValue();
      const data = JSON.parse(response);
      this.imageArray.push(...data );
      this.uploader.removeFromQueue(item);

      // because I'm using a Subject
      this.imagesSubject.next(this.imageArray);
    })
  ).subscribe();
}

ngOnDestroy() {
  this.onDestroySubj.next();
}