Search code examples
javascriptangularangular5observers

How to implement side effect on Observable retuned by HttpClient.get?


Does someone know how to use RxJS do operator on observer returned by angular's HttpClient.get method?

Versions:

  • angular: 5.2.1
  • rxjs: 5.5.6

With this version of angular I can't do this:

import { HttpClient } from '@angular/common/http';

...

constructor(
  private http: HttpClient,
) { }

...

this.http.get<Item>(url)
  .do(res => console.log(JSON.stringify(res)))
  .share();

Instead I have to add new steps with pipes. I can do it with switchMap, map or share, but I can't figure out how to do it with do.

I found out that I can do import { _do } from 'rxjs/operator/do';, but when I'm trying to use it like this:

this.http.get<Item>(url)
  .pipe(_do(res => console.log(JSON.stringify(res)))

or like this:

const observable = this.http.get<Item>(url);
observable.pipe(_do(observable, res => console.log(JSON.stringify(res)}));

I'm getting error:

[ts] The 'this' context of type 'void' is not assignable to method's 'this' of type 'Observable<{}>'.

Solution

  • As pipeable operators documentation explains, pipeable do was renamed to tap, alongside with several others. This was done in order to avoid collisions with reserved JavaScript keywords.

    Pipeable operators are supposed to be imported as

    import { tap } from 'rxjs/operators/tap';
    

    Notice that pipeable operators are located in rxjs/operators/..., while rxjs/operator/... imports are used to patch Observable.prototype.

    There are no concerns that would prevent do operator from being used some Angular versions. Both import styles are supported and valid, as long as a developer understands that there are certain differences between patch operators and pipeable operators that make the latter preferable in some cases. They are explained on documentation page:

    1. Any library that imports a patch operator will augment the Observable.prototype for all consumers of that library, creating blind dependencies. If the library removes their usage, they unknowingly break everyone else. With pipeables, you have to import the operators you need into each file you use them in.

    2. Operators patched directly onto the prototype are not "tree-shakeable" by tools like rollup or webpack. Pipeable operators will be as they are just functions pulled in from modules directly.

    3. Unused operators that are being imported in apps cannot be detected reliably by any sort of build tooling or lint rule. That means that you might import scan, but stop using it, and it's still being added to your output bundle. With pipeable operators, if you're not using it, a lint rule can pick it up for you.

    4. Functional composition is awesome. Building your own custom operators becomes much, much easier, and now they work and look just like all other operators from rxjs. You don't need to extend Observable or override lift anymore.