0

the following is an extension to observable to provide a safeSubscribe method that results in a subscription that each component's onDestroy method automatically unsubscribes.

// once _takeUntilDestroy$ on target emits, the subscription gets canceled
export function safeSubscribe<T>(target: any,
                                 next?: (value: T) => void,
                                 error?: (error: T) => void,
                                 complete?: () => void): Subscription {
  target._takeUntilDestroy$ = target._takeUntilDestroy$ || new Subject(); // our kill-switch for the subscription being defined
  return this.pipe(
    takeUntil(target._takeUntilDestroy$.asObservable()) // takeUntil enables the kill-switch
  ).subscribe(next, error, complete);
}

/****  add safeSubscribe to Observable ****/
(Observable as any).prototype.safeSubscribe = safeSubscribe;
declare module 'rxjs/internal/Observable' { // rxjs5 would be rxjs/Observable
 export interface Observable<T> {
    safeSubscribe: typeof safeSubscribe;
  }
}

I have taken the merge declaration part from this SO.

This implementation works perfectly when subscribing to Angular HTTPClient requests, but it fails when I try to safeSubscribe to observables created using factory methods like fromEvent:

fromEvent(this.fileInput.nativeElement, 'change').safeSubscribe<any>(this, (x) => { console.log(x); });

yielding a safeSubscribe is not a function error.

Am I extending Observable in the wrong way?

Output of npm list rxjs

+-- @angular-devkit/build-angular@0.6.3
| +-- @angular-devkit/architect@0.6.3
| | `-- rxjs@6.2.0  deduped
| +-- @angular-devkit/core@0.6.3
| | `-- rxjs@6.2.0  deduped
| `-- rxjs@6.2.0  deduped
+-- @angular/cli@6.0.3
| +-- @angular-devkit/schematics@0.6.3
| | `-- rxjs@6.2.0  deduped
| +-- @schematics/update@0.6.3
| | `-- rxjs@6.2.0  deduped
| `-- rxjs@6.2.0  deduped
`-- rxjs@6.2.0
Cec
  • 1,726
  • 3
  • 18
  • 31
  • @cartant I'm tagging you as I figured out the declaration merging thanks to your answer, hoping that you experienced this error already – Cec Jun 20 '18 at 16:31
  • There's nothing special about the factory methods. The most likely explanation is that there are multiple installations of RxJS and one is patched and the other is not. Run `npm list rxjs` to see what's installed in `node_modules`. – cartant Jun 20 '18 at 18:20
  • BTW, your signature should include `this`, like this: `safeSubscribe(this: Observable, target: any, ...` – cartant Jun 20 '18 at 18:22
  • @cartman thx, I will check this out tomorrow when I'm back at the office – Cec Jun 20 '18 at 19:16
  • @cartant from npm list rxjs I'd say that I have only one install. Maybe rxjs-compat is to blame, gonna try removing it now. Regarding the method signature, I call my implementation like anObservable.safeSubscribe(this, next, error, complete) as I want the current component. I don't get the reason for passing an observable as argument again – Cec Jun 25 '18 at 07:17
  • If you are adding it to the prototype, it should include a `this` parameter. The `this` isn't passed; it's included in the signature to indicate the type of the `this` context when called. Check the TypeScript docs for a more detailed explanation. – cartant Jun 25 '18 at 08:04
  • Hi @cartant, thanks for the pointer to `this`. Sadly I stil can't get that to work with observables created with `from` – Cec Jun 26 '18 at 07:52
  • I would encourage you to create a smaller reproduction of the problem, as - aside from the signature - you don't appear to be doing anything wrong. – cartant Jun 26 '18 at 07:56
  • You are right, the way I'm asking for help now is not structured at all. I'll make a stackblitz tonight and update my question. – Cec Jun 26 '18 at 08:20

0 Answers0