Комбинированные конвейерные операторы с RxJS/Angular

Возможно ли (я не могу найти ничего исчерпывающего по этому поводу) объединить несколько конвейерных операторов в одну функцию, чтобы я мог передать ее и повторно использовать в другом методе?

Вот в чем дело:

public ngOnInit(): void {
      this.route.url
            .pipe(
                switchMap( (ids: UrlSegment[]) => /* http call */),
                tap(result => /* setting a variable within a service */),
                tap(result => /* some more manipulating and logic */),
                map(result => /* setting a _this_ variable */)
            )
            .subscribe( () => {
                /* some other async tasks, which depend on _this_ variables */
                this.cdr.detectChanges();
            });
      }

Как мне извлечь все внутри pipe(), чтобы я мог вызывать ту же цепочку операторов из другого метода, который должен был бы выполнять тот же http-вызов и последующую логику и манипулирование?

Чего я пытаюсь достичь, так это:

 this.route.url
   .pipe(
       this.combinedPipableMethod(url: UrlSegment[])
   )
   .subscribe()

person pop    schedule 11.11.2019    source источник
comment
Я не уверен, почему вы установили значение члена класса (переменная _this_) на своей карте. Вы должны создать новые данные на основе данного выброса в файле map. Не выполнять побочные эффекты. Такое ощущение, что код в вашем map принадлежит вашему обратному вызову subscribe.   -  person realappie    schedule 11.11.2019
comment
Ты прав. Я тоже должен изменить его на tap.   -  person pop    schedule 11.11.2019
comment
Я думаю, вы должны просто выполнить эти действия в обратном вызове subscribe. Не вижу смысла иметь три tap. Лично я использую только tap для регистрации.   -  person realappie    schedule 11.11.2019
comment
Я действительно не знаю, какова наилучшая практика или первоначальное намерение для tapwas. На мой взгляд - если это что-то поставить и забыть, то в пределах tap более разборчиво. Я выбрал этот стиль для проекта, над которым недавно работал, и он мне очень нравится. В subscribe у меня есть дополнительные вызовы функций, которые имеют дело с контекстом this, но их нет в другой функции, которая повторно использует этот pipe, поэтому я бы оставил все как есть.   -  person pop    schedule 11.11.2019
comment
Пока мы этим занимаемся, не могли бы вы взглянуть на этот вопрос? :) stackoverflow.com/questions /58746929/   -  person pop    schedule 11.11.2019


Ответы (3)


Вы можете извлечь метод:

getData(ids: UrlSegment[]) {
   return this.http.get(/* url construction logic */)
      .pipe(
         tap(result => /* setting a variable within a service */),
         tap(result => /* some more manipulating and logic */),
         map(result => /* setting a _this_ variable */)
      );
}

А потом switchMap ему:

public ngOnInit(): void {
   this.route.url
      .pipe(
          switchMap(this.getData),
       )
       .subscribe( () => {
          /* some other async tasks, which depend on _this_ variables */
          this.cdr.detectChanges();
       });
}

В противном случае вы можете создать собственный оператор, но для этой цели это кажется излишним:

const combinedPipableMethod = () => {
  return source => defer(() => {
    return source.pipe(
       switchMap((ids: UrlSegment[]) => /* http call */),
       tap(result => /* setting a variable within a service */),
       tap(result => /* some more manipulating and logic */),
       map(result => /* setting a _this_ variable */)
    )
  })
}
public ngOnInit(): void {
   this.route.url
      .pipe(
          combinedPipableMethod(),
       )
       .subscribe( () => {
          /* some other async tasks, which depend on _this_ variables */
          this.cdr.detectChanges();
       });
}
person Vlad Vidac    schedule 11.11.2019
comment
Оба варианта звучат хорошо, а как насчет this контекста? Первый больше не знает никаких this... - person pop; 11.11.2019
comment
Не берите в голову. Я изменил обозначение на switchMap( urls => this.getData(urls)) , чтобы сохранить контекст - person pop; 11.11.2019

Вы можете использовать функцию rxjs pipe (обратите внимание, это отдельная функция, а не метод Observable), чтобы объединить последовательность операторов в один многократно используемый оператор.

import { pipe } from "rxjs";
 const customPipable = pipe(
     switchMap( (ids: UrlSegment[]) => /* http call */),
     tap(result => /* setting a variable within a service */),
     tap(result => /* some more manipulating and logic */),
     map(result => /* setting a _this_ variable */)
 )
  this.route.url
  .pipe(customPipable)
  .subscribe()

Вот статья об этом

person elad frizi    schedule 11.11.2019

Вы можете сохранить цепочку в Subject, а затем просто вызвать next() для субъекта, чтобы заставить конвейер работать.

//Subject for storing pipeline
loadDataSubject = new Subject();

ngOnInit() {
  loadDataPipe(this.loadDataSubject).subscribe(
     /* some other async tasks, which depend on _this_ variables */
     this.cdr.detectChanges();
  )
}

loadDataPipe(source) {
  return source.pipe(
       switchMap( (ids: UrlSegment[]) => /* http call */),
       tap(result => /* setting a variable within a service */),
       tap(result => /* some more manipulating and logic */),
       map(result => /* setting a _this_ variable */)
  )
}

Теперь не стесняйтесь снова запускать конвейер, где хотите, с помощью next():

 ....
 this.loadDataSubject.next();
 ....
person Ethan Vu    schedule 11.11.2019