pop
pop

Reputation: 3714

Combined pipable operators with RxJS / Angular

Is is possible (I can't find anything comprehensive on this) to combine multiple pipable operators in one function, so I can pipe it and re-use in a different method as well?

Here is the thing:

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

How do I extract everything within pipe() so I can call the same chain of operators from a different method, which would need to perform the same http call and subsequent logic and manipulating?

What I try to achieve is:

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

Upvotes: 1

Views: 709

Answers (3)

elad frizi
elad frizi

Reputation: 725

You can use rxjs pipe ( Notice, it's a standalone function, not the method of the Observable) function to combine a sequence of operators into one reusable operator.

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

Here is an article about it

Upvotes: 5

Ethan Vu
Ethan Vu

Reputation: 2987

You can store the chain within a Subject, then simply call next() on the subject to force the pipeline to run

//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 */)
  )
}

Now feel free to invoke the pipeline run again wherever you feel like with next():

 ....
 this.loadDataSubject.next();
 ....

Upvotes: 0

Vlad Vidac
Vlad Vidac

Reputation: 998

You could extract a method:

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 */)
      );
}

And then switchMap to it:

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

Otherwise, you could make a custom operator but it seems like overkill for this purpose:

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

Upvotes: 4

Related Questions