Создание Observable, которое привязывается к существующей функции - PullRequest
0 голосов
/ 25 февраля 2020

Я пытался связать функцию массива onCompleteItem из пакета ng2-file-upload с методом Rx JS 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);
  };

}

Моя попытка связать это в Observable была с помощью функции Rx JS bindCallback:

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();

Это не работает из-за нескольких ошибок типа / синтаксиса. У кого-нибудь есть хорошие предложения о том, как переписать это как связанную функцию Observable?

Ответы [ 2 ]

1 голос
/ 29 февраля 2020

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>
0 голосов
/ 27 марта 2020

Это было настоящее путешествие ... Но я надеюсь, что этот ответ даст некоторым пользователям новое понимание.

Вот несколько решений, с которыми я работал изначально, с последним перечисленным решением, которое я в итоге реализовал.

Исходное решение:

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();
}
...