@badisi/ngx-safe-subscribe 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  Automatically unsubscribe from RxJS observables in Angular components.
4
4
 
5
- [![npm](https://img.shields.io/npm/v/@badisi/ngx-safe-subscribe?color=blue&logo=npm)](https://www.npmjs.com/package/@badisi/ngx-safe-subscribe)
6
- [![Download](https://img.shields.io/npm/dw/@badisi/ngx-safe-subscribe.svg?color=7986CB&logo=npm)](https://npmcharts.com/compare/@badisi/ngx-safe-subscribe?minimal=true)
7
- [![License](https://img.shields.io/npm/l/@badisi/ngx-safe-subscribe.svg?color=ff69b4)](https://github.com/Badisi/ngx-safe-subscribe/blob/main/LICENSE)
5
+ [![npm version](https://img.shields.io/npm/v/@badisi/ngx-safe-subscribe?color=blue&logo=npm)][npm]
6
+ [![npm downloads](https://img.shields.io/npm/dw/@badisi/ngx-safe-subscribe.svg?color=7986CB&logo=npm)][npm-dl]
7
+ [![license](https://img.shields.io/npm/l/@badisi/ngx-safe-subscribe.svg?color=ff69b4)][license]
8
8
 
9
- <!--[![peerDependency Status](https://david-dm.org/badisi/ngx-safe-subscribe/peer-status.svg)](https://david-dm.org/badisi/ngx-safe-subscribe?type=peer)-->
9
+ [![build status](https://github.com/Badisi/ngx-safe-subscribe/actions/workflows/ci_tests.yml/badge.svg)][ci-tests]
10
+ [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)][pullrequest]
10
11
 
11
12
  :zap: *Angular <= 13 version available [here](https://github.com/Badisi/ngx-safe-subscribe/releases/tag/2.2.9)*
12
13
 
@@ -27,49 +28,68 @@ yarn add @badisi/ngx-safe-subscribe
27
28
 
28
29
  SafeSubscribe is an augmentation method of Observable.
29
30
 
30
- Calling **safeSubscribe** instead of **subscribe** will automatically unsubscribe your observable at component destroy.
31
+ Calling `safeSubscribe` instead of `subscribe` will automatically unsubscribe your observable at component destroy.
31
32
 
32
- ---------------------------------------
33
-
34
- :warning: At least a noop **ngOnDestroy** is required - but type checking will make sure you never forget about it :wink:
35
-
36
- ---------------------------------------
37
-
38
- ### safeSubscribe(target: any, next: Function, error: Function, complete: Function): Subscription
39
-
40
- __Arguments__
41
-
42
- * `target` - A reference to the object that is holding the observable.
43
- * `next` - A handler for each delivered value. Called zero or more times after execution starts.
44
- * `error` - A handler for an error notification. An error halts execution of the observable instance.
45
- * `complete` - A handler for the execution-complete notification. Delayed values can continue to be delivered to the next handler after execution is complete.
46
-
47
- __Return__
48
-
49
- * A `Subscription` object.
50
-
51
- __Example__
33
+ #### Example with Angular components
52
34
 
53
35
  ```ts
54
- import { Component, OnInit, OnDestroy } from '@angular/core';
55
- import { Observable, interval } from 'rxjs';
56
- import '@badisi/ngx-safe-subscribe';
36
+ import { Component, OnInit } from '@angular/core';
37
+ import { SafeSubscribe } from '@badisi/ngx-safe-subscribe';
38
+ import { interval } from 'rxjs';
57
39
 
40
+ @SafeSubscribe()
58
41
  @Component({
59
42
  selector: 'app-component'
60
43
  })
61
- export class AppComponent implements OnInit, OnDestroy {
44
+ export class AppComponent implements OnInit {
62
45
  ngOnInit() {
63
46
  interval(1000).safeSubscribe(this, () => {
64
47
  console.log('This log will stop on component destroy.')
65
48
  });
66
49
  }
50
+ }
51
+ ```
52
+
53
+ #### Example with simple class objects
67
54
 
68
- // /!\ At least a noop ngOnDestroy is required for SafeSubscribe to work !
69
- ngOnDestroy() {}
55
+ ```ts
56
+ import { SafeSubscribe } from '@badisi/ngx-safe-subscribe';
57
+ import { interval } from 'rxjs';
58
+
59
+ @SafeSubscribe('destroy')
60
+ export class MyObject {
61
+ constructor() {
62
+ interval(1000).safeSubscribe(this, () => {
63
+ console.log('This log will stop on object destroy.')
64
+ });
65
+ }
66
+ destroy() {}
70
67
  }
71
68
  ```
72
69
 
70
+ ## Api
71
+
72
+ ### @SafeSubscribe(destructorName)
73
+
74
+ __Arguments__
75
+
76
+ * `destructorName: string` *(default: "ngOnDestroy")* - The name of the method that will called when the object is supposed to be destroyed.
77
+
78
+ ---
79
+
80
+ ### Observable.safeSubscribe(target, ...arguments): Subscription
81
+
82
+ __Arguments__
83
+
84
+ * `target: any` - A reference to the object that is holding the observable.
85
+ * `observerOrNext?: Observer|Function` - Either an observer with methods to be called, or the first of three possible handlers, which is the handler for each value emitted from the subscribed Observable.
86
+ * `error?: Function` - A handler for a terminal event resulting from an error. If no error handler is provided, the error will be thrown asynchronously as unhandled.
87
+ * `complete?: Function` - A handler for a terminal event resulting from successful completion.
88
+
89
+ __Return__
90
+
91
+ * A `Subscription` reference to the registered handler.
92
+
73
93
  ## Purpose
74
94
 
75
95
  To quote a great [article](https://netbasal.com/when-to-unsubscribe-in-angular-d61c6b21bad3) from **Netanel Basal** :
@@ -94,3 +114,26 @@ There are a few exceptional observables where you don't need to unsubscribe :
94
114
  However, as stated in the official Angular documentation :
95
115
 
96
116
  > Feel free to unsubscribe anyway. It is harmless and never a bad practice !
117
+
118
+ ## Contributing
119
+
120
+ #### > Want to Help ?
121
+
122
+ Want to file a bug, contribute some code or improve documentation ? Excellent!
123
+
124
+ But please read up first on the guidelines for [contributing][contributing], and learn about submission process, coding rules and more.
125
+
126
+ #### > Code of Conduct
127
+
128
+ Please read and follow the [Code of Conduct][codeofconduct] and help me keep this project open and inclusive.
129
+
130
+
131
+
132
+
133
+ [npm]: https://www.npmjs.com/package/@badisi/ngx-safe-subscribe
134
+ [npm-dl]: https://npmcharts.com/compare/@badisi/ngx-safe-subscribe?minimal=true
135
+ [ci-tests]: https://github.com/Badisi/ngx-safe-subscribe/actions/workflows/ci_tests.yml
136
+ [pullrequest]: https://github.com/badisi/ngx-safe-subscribe/blob/main/CONTRIBUTING.md#-submitting-a-pull-request-pr
137
+ [license]: https://github.com/Badisi/ngx-safe-subscribe/blob/main/LICENSE
138
+ [contributing]: https://github.com/badisi/latest-version/blob/main/CONTRIBUTING.md
139
+ [codeofconduct]: https://github.com/badisi/latest-version/blob/main/CODE_OF_CONDUCT.md
@@ -1,23 +1,30 @@
1
1
  import { Observable, Subscription } from 'rxjs';
2
- export function safeSubscribe(target, next, error, complete) {
3
- const sub = this.subscribe(next, error, complete);
2
+ const HAS_DECORATOR = Symbol('__safeSubscribeDecorator');
3
+ const SUBSCRIPTION = Symbol('__safeSubscribeSubscription$');
4
+ export function SafeSubscribe(destructorName = 'ngOnDestroy') {
5
+ return (classType) => {
6
+ const originalDestroy = classType.prototype[destructorName];
7
+ classType.prototype[destructorName] = function () {
8
+ originalDestroy && originalDestroy.call(this);
9
+ this[SUBSCRIPTION]?.unsubscribe();
10
+ this[SUBSCRIPTION] = null;
11
+ };
12
+ classType.prototype[HAS_DECORATOR] = true;
13
+ };
14
+ }
15
+ export function safeSubscribe(target, ...args) {
16
+ const sub = this.subscribe(...args);
4
17
  if (target) {
5
- if (!('_subscriptionFromSafeSubscribe$' in target)) {
6
- target._subscriptionFromSafeSubscribe$ = new Subscription();
7
- const originalDestroy = target.ngOnDestroy;
8
- if (!originalDestroy) {
9
- console.warn(`${target.constructor.name} must implement OnDestroy otherwise Observable<T>.safeSubscribe will have no effect.`);
10
- }
11
- target.ngOnDestroy = function () {
12
- if (originalDestroy && (typeof originalDestroy === 'function')) {
13
- originalDestroy.apply(this, arguments);
14
- }
15
- target._subscriptionFromSafeSubscribe$.unsubscribe();
16
- };
18
+ const hasDecorator = Object.getPrototypeOf(target)[HAS_DECORATOR];
19
+ if (!hasDecorator) {
20
+ throw new Error(`${target.constructor.name} class must be decorated with @SafeSubscribe() otherwise Observable<T>.safeSubscribe() will have no effect.`);
21
+ }
22
+ if (!target[SUBSCRIPTION]) {
23
+ target[SUBSCRIPTION] = new Subscription();
17
24
  }
18
- target._subscriptionFromSafeSubscribe$.add(sub);
25
+ target[SUBSCRIPTION].add(sub);
19
26
  }
20
27
  return sub;
21
28
  }
22
29
  Observable.prototype.safeSubscribe = safeSubscribe;
23
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LXNhZmUtc3Vic2NyaWJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbmd4LXNhZmUtc3Vic2NyaWJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBYWhELE1BQU0sVUFBVSxhQUFhLENBQ3pCLE1BQXdCLEVBQ3hCLElBQXlCLEVBQ3pCLEtBQTRCLEVBQzVCLFFBQXFCO0lBRXJCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNsRCxJQUFJLE1BQU0sRUFBRTtRQUNSLElBQUksQ0FBQyxDQUFDLGlDQUFpQyxJQUFJLE1BQU0sQ0FBQyxFQUFFO1lBQ2hELE1BQU0sQ0FBQywrQkFBK0IsR0FBRyxJQUFJLFlBQVksRUFBRSxDQUFDO1lBRTVELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUM7WUFDM0MsSUFBSSxDQUFDLGVBQWUsRUFBRTtnQkFDbEIsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFJLE1BQWMsQ0FBQyxXQUFXLENBQUMsSUFBSSxzRkFBc0YsQ0FBQyxDQUFDO2FBQzNJO1lBQ0QsTUFBTSxDQUFDLFdBQVcsR0FBRztnQkFDakIsSUFBSSxlQUFlLElBQUksQ0FBQyxPQUFPLGVBQWUsS0FBSyxVQUFVLENBQUMsRUFBRTtvQkFDNUQsZUFBZSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7aUJBQzFDO2dCQUNELE1BQU0sQ0FBQywrQkFBK0IsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUN6RCxDQUFDLENBQUM7U0FDTDtRQUNELE1BQU0sQ0FBQywrQkFBK0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7S0FDbkQ7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNmLENBQUM7QUFDRCxVQUFVLENBQUMsU0FBUyxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBPYnNlcnZhYmxlLCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcblxuZGVjbGFyZSBtb2R1bGUgJ3J4anMvaW50ZXJuYWwvT2JzZXJ2YWJsZScge1xuICAgIGludGVyZmFjZSBPYnNlcnZhYmxlPFQ+IHtcbiAgICAgICAgc2FmZVN1YnNjcmliZTogdHlwZW9mIHNhZmVTdWJzY3JpYmU7XG4gICAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNhZmVTdWJzY3JpYmFibGUge1xuICAgIF9zdWJzY3JpcHRpb25Gcm9tU2FmZVN1YnNjcmliZSQ/OiBTdWJzY3JpcHRpb247XG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNhZmVTdWJzY3JpYmU8VD4oXG4gICAgdGFyZ2V0OiBTYWZlU3Vic2NyaWJhYmxlLFxuICAgIG5leHQ/OiAodmFsdWU6IFQpID0+IHZvaWQsXG4gICAgZXJyb3I/OiAoZXJyb3I6IGFueSkgPT4gdm9pZCxcbiAgICBjb21wbGV0ZT86ICgpID0+IHZvaWRcbik6IFN1YnNjcmlwdGlvbiB7XG4gICAgY29uc3Qgc3ViID0gdGhpcy5zdWJzY3JpYmUobmV4dCwgZXJyb3IsIGNvbXBsZXRlKTtcbiAgICBpZiAodGFyZ2V0KSB7XG4gICAgICAgIGlmICghKCdfc3Vic2NyaXB0aW9uRnJvbVNhZmVTdWJzY3JpYmUkJyBpbiB0YXJnZXQpKSB7XG4gICAgICAgICAgICB0YXJnZXQuX3N1YnNjcmlwdGlvbkZyb21TYWZlU3Vic2NyaWJlJCA9IG5ldyBTdWJzY3JpcHRpb24oKTtcblxuICAgICAgICAgICAgY29uc3Qgb3JpZ2luYWxEZXN0cm95ID0gdGFyZ2V0Lm5nT25EZXN0cm95O1xuICAgICAgICAgICAgaWYgKCFvcmlnaW5hbERlc3Ryb3kpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYCR7KHRhcmdldCBhcyBhbnkpLmNvbnN0cnVjdG9yLm5hbWV9IG11c3QgaW1wbGVtZW50IE9uRGVzdHJveSBvdGhlcndpc2UgT2JzZXJ2YWJsZTxUPi5zYWZlU3Vic2NyaWJlIHdpbGwgaGF2ZSBubyBlZmZlY3QuYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0YXJnZXQubmdPbkRlc3Ryb3kgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgaWYgKG9yaWdpbmFsRGVzdHJveSAmJiAodHlwZW9mIG9yaWdpbmFsRGVzdHJveSA9PT0gJ2Z1bmN0aW9uJykpIHtcbiAgICAgICAgICAgICAgICAgICAgb3JpZ2luYWxEZXN0cm95LmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRhcmdldC5fc3Vic2NyaXB0aW9uRnJvbVNhZmVTdWJzY3JpYmUkLnVuc3Vic2NyaWJlKCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIHRhcmdldC5fc3Vic2NyaXB0aW9uRnJvbVNhZmVTdWJzY3JpYmUkLmFkZChzdWIpO1xuICAgIH1cbiAgICByZXR1cm4gc3ViO1xufVxuT2JzZXJ2YWJsZS5wcm90b3R5cGUuc2FmZVN1YnNjcmliZSA9IHNhZmVTdWJzY3JpYmU7XG4iXX0=
30
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LXNhZmUtc3Vic2NyaWJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbmd4LXNhZmUtc3Vic2NyaWJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRWhELE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0FBQ3pELE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0FBUTVELE1BQU0sVUFBVSxhQUFhLENBQUMsY0FBYyxHQUFFLGFBQWE7SUFDdkQsT0FBTyxDQUFDLFNBQW1CLEVBQUUsRUFBRTtRQUMzQixNQUFNLGVBQWUsR0FBRyxTQUFTLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzVELFNBQVMsQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLEdBQUc7WUFDbEMsZUFBZSxJQUFJLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDOUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDO1lBQ2xDLElBQUksQ0FBQyxZQUFZLENBQUMsR0FBRyxJQUFJLENBQUM7UUFDOUIsQ0FBQyxDQUFBO1FBQ0QsU0FBUyxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsR0FBRyxJQUFJLENBQUM7SUFDOUMsQ0FBQyxDQUFDO0FBQ04sQ0FBQztBQUVELE1BQU0sVUFBVSxhQUFhLENBQXlCLE1BQVcsRUFBRSxHQUFHLElBQVM7SUFDM0UsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO0lBQ3BDLElBQUksTUFBTSxFQUFFO1FBQ1IsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUMsWUFBWSxFQUFFO1lBQ2YsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSw2R0FBNkcsQ0FBQyxDQUFDO1NBQzVKO1FBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRTtZQUN2QixNQUFNLENBQUMsWUFBWSxDQUFDLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztTQUM3QztRQUNELE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7S0FDakM7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNmLENBQUM7QUFDRCxVQUFVLENBQUMsU0FBUyxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBPYnNlcnZhYmxlLCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcblxuY29uc3QgSEFTX0RFQ09SQVRPUiA9IFN5bWJvbCgnX19zYWZlU3Vic2NyaWJlRGVjb3JhdG9yJyk7XG5jb25zdCBTVUJTQ1JJUFRJT04gPSBTeW1ib2woJ19fc2FmZVN1YnNjcmliZVN1YnNjcmlwdGlvbiQnKTtcblxuZGVjbGFyZSBtb2R1bGUgJ3J4anMvaW50ZXJuYWwvT2JzZXJ2YWJsZScge1xuICAgIGludGVyZmFjZSBPYnNlcnZhYmxlPFQ+IHtcbiAgICAgICAgc2FmZVN1YnNjcmliZTogdHlwZW9mIHNhZmVTdWJzY3JpYmU7XG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gU2FmZVN1YnNjcmliZShkZXN0cnVjdG9yTmFtZSA9J25nT25EZXN0cm95Jyk6IENsYXNzRGVjb3JhdG9yIHtcbiAgICByZXR1cm4gKGNsYXNzVHlwZTogRnVuY3Rpb24pID0+IHtcbiAgICAgICAgY29uc3Qgb3JpZ2luYWxEZXN0cm95ID0gY2xhc3NUeXBlLnByb3RvdHlwZVtkZXN0cnVjdG9yTmFtZV07XG4gICAgICAgIGNsYXNzVHlwZS5wcm90b3R5cGVbZGVzdHJ1Y3Rvck5hbWVdID0gZnVuY3Rpb24gKHRoaXM6IGFueSkge1xuICAgICAgICAgICAgb3JpZ2luYWxEZXN0cm95ICYmIG9yaWdpbmFsRGVzdHJveS5jYWxsKHRoaXMpO1xuICAgICAgICAgICAgdGhpc1tTVUJTQ1JJUFRJT05dPy51bnN1YnNjcmliZSgpO1xuICAgICAgICAgICAgdGhpc1tTVUJTQ1JJUFRJT05dID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBjbGFzc1R5cGUucHJvdG90eXBlW0hBU19ERUNPUkFUT1JdID0gdHJ1ZTtcbiAgICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2FmZVN1YnNjcmliZTxUPih0aGlzOiBPYnNlcnZhYmxlPFQ+LCB0YXJnZXQ6IGFueSwgLi4uYXJnczogYW55KTogU3Vic2NyaXB0aW9uIHtcbiAgICBjb25zdCBzdWIgPSB0aGlzLnN1YnNjcmliZSguLi5hcmdzKTtcbiAgICBpZiAodGFyZ2V0KSB7XG4gICAgICAgIGNvbnN0IGhhc0RlY29yYXRvciA9IE9iamVjdC5nZXRQcm90b3R5cGVPZih0YXJnZXQpW0hBU19ERUNPUkFUT1JdO1xuICAgICAgICBpZiAoIWhhc0RlY29yYXRvcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGAke3RhcmdldC5jb25zdHJ1Y3Rvci5uYW1lfSBjbGFzcyBtdXN0IGJlIGRlY29yYXRlZCB3aXRoIEBTYWZlU3Vic2NyaWJlKCkgb3RoZXJ3aXNlIE9ic2VydmFibGU8VD4uc2FmZVN1YnNjcmliZSgpIHdpbGwgaGF2ZSBubyBlZmZlY3QuYCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCF0YXJnZXRbU1VCU0NSSVBUSU9OXSkge1xuICAgICAgICAgICAgdGFyZ2V0W1NVQlNDUklQVElPTl0gPSBuZXcgU3Vic2NyaXB0aW9uKCk7XG4gICAgICAgIH1cbiAgICAgICAgdGFyZ2V0W1NVQlNDUklQVElPTl0uYWRkKHN1Yik7XG4gICAgfVxuICAgIHJldHVybiBzdWI7XG59XG5PYnNlcnZhYmxlLnByb3RvdHlwZS5zYWZlU3Vic2NyaWJlID0gc2FmZVN1YnNjcmliZTtcbiJdfQ==
@@ -1,22 +1,30 @@
1
1
  import { Subscription, Observable } from 'rxjs';
2
2
 
3
- function safeSubscribe(target, next, error, complete) {
4
- const sub = this.subscribe(next, error, complete);
3
+ const HAS_DECORATOR = Symbol('__safeSubscribeDecorator');
4
+ const SUBSCRIPTION = Symbol('__safeSubscribeSubscription$');
5
+ function SafeSubscribe(destructorName = 'ngOnDestroy') {
6
+ return (classType) => {
7
+ const originalDestroy = classType.prototype[destructorName];
8
+ classType.prototype[destructorName] = function () {
9
+ var _a;
10
+ originalDestroy && originalDestroy.call(this);
11
+ (_a = this[SUBSCRIPTION]) === null || _a === void 0 ? void 0 : _a.unsubscribe();
12
+ this[SUBSCRIPTION] = null;
13
+ };
14
+ classType.prototype[HAS_DECORATOR] = true;
15
+ };
16
+ }
17
+ function safeSubscribe(target, ...args) {
18
+ const sub = this.subscribe(...args);
5
19
  if (target) {
6
- if (!('_subscriptionFromSafeSubscribe$' in target)) {
7
- target._subscriptionFromSafeSubscribe$ = new Subscription();
8
- const originalDestroy = target.ngOnDestroy;
9
- if (!originalDestroy) {
10
- console.warn(`${target.constructor.name} must implement OnDestroy otherwise Observable<T>.safeSubscribe will have no effect.`);
11
- }
12
- target.ngOnDestroy = function () {
13
- if (originalDestroy && (typeof originalDestroy === 'function')) {
14
- originalDestroy.apply(this, arguments);
15
- }
16
- target._subscriptionFromSafeSubscribe$.unsubscribe();
17
- };
20
+ const hasDecorator = Object.getPrototypeOf(target)[HAS_DECORATOR];
21
+ if (!hasDecorator) {
22
+ throw new Error(`${target.constructor.name} class must be decorated with @SafeSubscribe() otherwise Observable<T>.safeSubscribe() will have no effect.`);
23
+ }
24
+ if (!target[SUBSCRIPTION]) {
25
+ target[SUBSCRIPTION] = new Subscription();
18
26
  }
19
- target._subscriptionFromSafeSubscribe$.add(sub);
27
+ target[SUBSCRIPTION].add(sub);
20
28
  }
21
29
  return sub;
22
30
  }
@@ -26,5 +34,5 @@ Observable.prototype.safeSubscribe = safeSubscribe;
26
34
  * Generated bundle index. Do not edit.
27
35
  */
28
36
 
29
- export { safeSubscribe };
37
+ export { SafeSubscribe, safeSubscribe };
30
38
  //# sourceMappingURL=badisi-ngx-safe-subscribe.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"badisi-ngx-safe-subscribe.mjs","sources":["../../ngx-safe-subscribe.ts","../../badisi-ngx-safe-subscribe.ts"],"sourcesContent":["import { Observable, Subscription } from 'rxjs';\n\ndeclare module 'rxjs/internal/Observable' {\n interface Observable<T> {\n safeSubscribe: typeof safeSubscribe;\n }\n}\n\nexport interface SafeSubscribable {\n _subscriptionFromSafeSubscribe$?: Subscription;\n ngOnDestroy(): void;\n}\n\nexport function safeSubscribe<T>(\n target: SafeSubscribable,\n next?: (value: T) => void,\n error?: (error: any) => void,\n complete?: () => void\n): Subscription {\n const sub = this.subscribe(next, error, complete);\n if (target) {\n if (!('_subscriptionFromSafeSubscribe$' in target)) {\n target._subscriptionFromSafeSubscribe$ = new Subscription();\n\n const originalDestroy = target.ngOnDestroy;\n if (!originalDestroy) {\n console.warn(`${(target as any).constructor.name} must implement OnDestroy otherwise Observable<T>.safeSubscribe will have no effect.`);\n }\n target.ngOnDestroy = function () {\n if (originalDestroy && (typeof originalDestroy === 'function')) {\n originalDestroy.apply(this, arguments);\n }\n target._subscriptionFromSafeSubscribe$.unsubscribe();\n };\n }\n target._subscriptionFromSafeSubscribe$.add(sub);\n }\n return sub;\n}\nObservable.prototype.safeSubscribe = safeSubscribe;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './ngx-safe-subscribe';\n"],"names":[],"mappings":";;AAaM,SAAU,aAAa,CACzB,MAAwB,EACxB,IAAyB,EACzB,KAA4B,EAC5B,QAAqB,EAAA;AAErB,IAAA,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClD,IAAA,IAAI,MAAM,EAAE;AACR,QAAA,IAAI,EAAE,iCAAiC,IAAI,MAAM,CAAC,EAAE;AAChD,YAAA,MAAM,CAAC,+BAA+B,GAAG,IAAI,YAAY,EAAE,CAAC;AAE5D,YAAA,MAAM,eAAe,GAAG,MAAM,CAAC,WAAW,CAAC;YAC3C,IAAI,CAAC,eAAe,EAAE;gBAClB,OAAO,CAAC,IAAI,CAAC,CAAI,EAAA,MAAc,CAAC,WAAW,CAAC,IAAI,CAAsF,oFAAA,CAAA,CAAC,CAAC;AAC3I,aAAA;YACD,MAAM,CAAC,WAAW,GAAG,YAAA;gBACjB,IAAI,eAAe,KAAK,OAAO,eAAe,KAAK,UAAU,CAAC,EAAE;AAC5D,oBAAA,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC1C,iBAAA;AACD,gBAAA,MAAM,CAAC,+BAA+B,CAAC,WAAW,EAAE,CAAC;AACzD,aAAC,CAAC;AACL,SAAA;AACD,QAAA,MAAM,CAAC,+BAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACnD,KAAA;AACD,IAAA,OAAO,GAAG,CAAC;AACf,CAAC;AACD,UAAU,CAAC,SAAS,CAAC,aAAa,GAAG,aAAa;;ACvClD;;AAEG;;;;"}
1
+ {"version":3,"file":"badisi-ngx-safe-subscribe.mjs","sources":["../../ngx-safe-subscribe.ts","../../badisi-ngx-safe-subscribe.ts"],"sourcesContent":["import { Observable, Subscription } from 'rxjs';\n\nconst HAS_DECORATOR = Symbol('__safeSubscribeDecorator');\nconst SUBSCRIPTION = Symbol('__safeSubscribeSubscription$');\n\ndeclare module 'rxjs/internal/Observable' {\n interface Observable<T> {\n safeSubscribe: typeof safeSubscribe;\n }\n}\n\nexport function SafeSubscribe(destructorName ='ngOnDestroy'): ClassDecorator {\n return (classType: Function) => {\n const originalDestroy = classType.prototype[destructorName];\n classType.prototype[destructorName] = function (this: any) {\n originalDestroy && originalDestroy.call(this);\n this[SUBSCRIPTION]?.unsubscribe();\n this[SUBSCRIPTION] = null;\n }\n classType.prototype[HAS_DECORATOR] = true;\n };\n}\n\nexport function safeSubscribe<T>(this: Observable<T>, target: any, ...args: any): Subscription {\n const sub = this.subscribe(...args);\n if (target) {\n const hasDecorator = Object.getPrototypeOf(target)[HAS_DECORATOR];\n if (!hasDecorator) {\n throw new Error(`${target.constructor.name} class must be decorated with @SafeSubscribe() otherwise Observable<T>.safeSubscribe() will have no effect.`);\n }\n if (!target[SUBSCRIPTION]) {\n target[SUBSCRIPTION] = new Subscription();\n }\n target[SUBSCRIPTION].add(sub);\n }\n return sub;\n}\nObservable.prototype.safeSubscribe = safeSubscribe;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './ngx-safe-subscribe';\n"],"names":[],"mappings":";;AAEA,MAAM,aAAa,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC;AACzD,MAAM,YAAY,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC;AAQ5C,SAAA,aAAa,CAAC,cAAc,GAAE,aAAa,EAAA;IACvD,OAAO,CAAC,SAAmB,KAAI;QAC3B,MAAM,eAAe,GAAG,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;AAC5D,QAAA,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,YAAA;;AAClC,YAAA,eAAe,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,YAAA,CAAA,EAAA,GAAA,IAAI,CAAC,YAAY,CAAC,MAAE,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,WAAW,EAAE,CAAC;AAClC,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;AAC9B,SAAC,CAAA;AACD,QAAA,SAAS,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;AAC9C,KAAC,CAAC;AACN,CAAC;SAEe,aAAa,CAAyB,MAAW,EAAE,GAAG,IAAS,EAAA;IAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AACpC,IAAA,IAAI,MAAM,EAAE;QACR,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;QAClE,IAAI,CAAC,YAAY,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,CAAG,EAAA,MAAM,CAAC,WAAW,CAAC,IAAI,CAA6G,2GAAA,CAAA,CAAC,CAAC;AAC5J,SAAA;AACD,QAAA,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;AACvB,YAAA,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,YAAY,EAAE,CAAC;AAC7C,SAAA;QACD,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACjC,KAAA;AACD,IAAA,OAAO,GAAG,CAAC;AACf,CAAC;AACD,UAAU,CAAC,SAAS,CAAC,aAAa,GAAG,aAAa;;ACrClD;;AAEG;;;;"}
@@ -1,22 +1,29 @@
1
1
  import { Subscription, Observable } from 'rxjs';
2
2
 
3
- function safeSubscribe(target, next, error, complete) {
4
- const sub = this.subscribe(next, error, complete);
3
+ const HAS_DECORATOR = Symbol('__safeSubscribeDecorator');
4
+ const SUBSCRIPTION = Symbol('__safeSubscribeSubscription$');
5
+ function SafeSubscribe(destructorName = 'ngOnDestroy') {
6
+ return (classType) => {
7
+ const originalDestroy = classType.prototype[destructorName];
8
+ classType.prototype[destructorName] = function () {
9
+ originalDestroy && originalDestroy.call(this);
10
+ this[SUBSCRIPTION]?.unsubscribe();
11
+ this[SUBSCRIPTION] = null;
12
+ };
13
+ classType.prototype[HAS_DECORATOR] = true;
14
+ };
15
+ }
16
+ function safeSubscribe(target, ...args) {
17
+ const sub = this.subscribe(...args);
5
18
  if (target) {
6
- if (!('_subscriptionFromSafeSubscribe$' in target)) {
7
- target._subscriptionFromSafeSubscribe$ = new Subscription();
8
- const originalDestroy = target.ngOnDestroy;
9
- if (!originalDestroy) {
10
- console.warn(`${target.constructor.name} must implement OnDestroy otherwise Observable<T>.safeSubscribe will have no effect.`);
11
- }
12
- target.ngOnDestroy = function () {
13
- if (originalDestroy && (typeof originalDestroy === 'function')) {
14
- originalDestroy.apply(this, arguments);
15
- }
16
- target._subscriptionFromSafeSubscribe$.unsubscribe();
17
- };
19
+ const hasDecorator = Object.getPrototypeOf(target)[HAS_DECORATOR];
20
+ if (!hasDecorator) {
21
+ throw new Error(`${target.constructor.name} class must be decorated with @SafeSubscribe() otherwise Observable<T>.safeSubscribe() will have no effect.`);
22
+ }
23
+ if (!target[SUBSCRIPTION]) {
24
+ target[SUBSCRIPTION] = new Subscription();
18
25
  }
19
- target._subscriptionFromSafeSubscribe$.add(sub);
26
+ target[SUBSCRIPTION].add(sub);
20
27
  }
21
28
  return sub;
22
29
  }
@@ -26,5 +33,5 @@ Observable.prototype.safeSubscribe = safeSubscribe;
26
33
  * Generated bundle index. Do not edit.
27
34
  */
28
35
 
29
- export { safeSubscribe };
36
+ export { SafeSubscribe, safeSubscribe };
30
37
  //# sourceMappingURL=badisi-ngx-safe-subscribe.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"badisi-ngx-safe-subscribe.mjs","sources":["../../ngx-safe-subscribe.ts","../../badisi-ngx-safe-subscribe.ts"],"sourcesContent":["import { Observable, Subscription } from 'rxjs';\n\ndeclare module 'rxjs/internal/Observable' {\n interface Observable<T> {\n safeSubscribe: typeof safeSubscribe;\n }\n}\n\nexport interface SafeSubscribable {\n _subscriptionFromSafeSubscribe$?: Subscription;\n ngOnDestroy(): void;\n}\n\nexport function safeSubscribe<T>(\n target: SafeSubscribable,\n next?: (value: T) => void,\n error?: (error: any) => void,\n complete?: () => void\n): Subscription {\n const sub = this.subscribe(next, error, complete);\n if (target) {\n if (!('_subscriptionFromSafeSubscribe$' in target)) {\n target._subscriptionFromSafeSubscribe$ = new Subscription();\n\n const originalDestroy = target.ngOnDestroy;\n if (!originalDestroy) {\n console.warn(`${(target as any).constructor.name} must implement OnDestroy otherwise Observable<T>.safeSubscribe will have no effect.`);\n }\n target.ngOnDestroy = function () {\n if (originalDestroy && (typeof originalDestroy === 'function')) {\n originalDestroy.apply(this, arguments);\n }\n target._subscriptionFromSafeSubscribe$.unsubscribe();\n };\n }\n target._subscriptionFromSafeSubscribe$.add(sub);\n }\n return sub;\n}\nObservable.prototype.safeSubscribe = safeSubscribe;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './ngx-safe-subscribe';\n"],"names":[],"mappings":";;AAaM,SAAU,aAAa,CACzB,MAAwB,EACxB,IAAyB,EACzB,KAA4B,EAC5B,QAAqB,EAAA;AAErB,IAAA,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClD,IAAA,IAAI,MAAM,EAAE;AACR,QAAA,IAAI,EAAE,iCAAiC,IAAI,MAAM,CAAC,EAAE;AAChD,YAAA,MAAM,CAAC,+BAA+B,GAAG,IAAI,YAAY,EAAE,CAAC;AAE5D,YAAA,MAAM,eAAe,GAAG,MAAM,CAAC,WAAW,CAAC;YAC3C,IAAI,CAAC,eAAe,EAAE;gBAClB,OAAO,CAAC,IAAI,CAAC,CAAI,EAAA,MAAc,CAAC,WAAW,CAAC,IAAI,CAAsF,oFAAA,CAAA,CAAC,CAAC;AAC3I,aAAA;YACD,MAAM,CAAC,WAAW,GAAG,YAAA;gBACjB,IAAI,eAAe,KAAK,OAAO,eAAe,KAAK,UAAU,CAAC,EAAE;AAC5D,oBAAA,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC1C,iBAAA;AACD,gBAAA,MAAM,CAAC,+BAA+B,CAAC,WAAW,EAAE,CAAC;AACzD,aAAC,CAAC;AACL,SAAA;AACD,QAAA,MAAM,CAAC,+BAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACnD,KAAA;AACD,IAAA,OAAO,GAAG,CAAC;AACf,CAAC;AACD,UAAU,CAAC,SAAS,CAAC,aAAa,GAAG,aAAa;;ACvClD;;AAEG;;;;"}
1
+ {"version":3,"file":"badisi-ngx-safe-subscribe.mjs","sources":["../../ngx-safe-subscribe.ts","../../badisi-ngx-safe-subscribe.ts"],"sourcesContent":["import { Observable, Subscription } from 'rxjs';\n\nconst HAS_DECORATOR = Symbol('__safeSubscribeDecorator');\nconst SUBSCRIPTION = Symbol('__safeSubscribeSubscription$');\n\ndeclare module 'rxjs/internal/Observable' {\n interface Observable<T> {\n safeSubscribe: typeof safeSubscribe;\n }\n}\n\nexport function SafeSubscribe(destructorName ='ngOnDestroy'): ClassDecorator {\n return (classType: Function) => {\n const originalDestroy = classType.prototype[destructorName];\n classType.prototype[destructorName] = function (this: any) {\n originalDestroy && originalDestroy.call(this);\n this[SUBSCRIPTION]?.unsubscribe();\n this[SUBSCRIPTION] = null;\n }\n classType.prototype[HAS_DECORATOR] = true;\n };\n}\n\nexport function safeSubscribe<T>(this: Observable<T>, target: any, ...args: any): Subscription {\n const sub = this.subscribe(...args);\n if (target) {\n const hasDecorator = Object.getPrototypeOf(target)[HAS_DECORATOR];\n if (!hasDecorator) {\n throw new Error(`${target.constructor.name} class must be decorated with @SafeSubscribe() otherwise Observable<T>.safeSubscribe() will have no effect.`);\n }\n if (!target[SUBSCRIPTION]) {\n target[SUBSCRIPTION] = new Subscription();\n }\n target[SUBSCRIPTION].add(sub);\n }\n return sub;\n}\nObservable.prototype.safeSubscribe = safeSubscribe;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './ngx-safe-subscribe';\n"],"names":[],"mappings":";;AAEA,MAAM,aAAa,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC;AACzD,MAAM,YAAY,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC;AAQ5C,SAAA,aAAa,CAAC,cAAc,GAAE,aAAa,EAAA;IACvD,OAAO,CAAC,SAAmB,KAAI;QAC3B,MAAM,eAAe,GAAG,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;AAC5D,QAAA,SAAS,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,YAAA;AAClC,YAAA,eAAe,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,YAAA,IAAI,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;AAClC,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;AAC9B,SAAC,CAAA;AACD,QAAA,SAAS,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;AAC9C,KAAC,CAAC;AACN,CAAC;SAEe,aAAa,CAAyB,MAAW,EAAE,GAAG,IAAS,EAAA;IAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AACpC,IAAA,IAAI,MAAM,EAAE;QACR,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;QAClE,IAAI,CAAC,YAAY,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,CAAG,EAAA,MAAM,CAAC,WAAW,CAAC,IAAI,CAA6G,2GAAA,CAAA,CAAC,CAAC;AAC5J,SAAA;AACD,QAAA,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;AACvB,YAAA,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,YAAY,EAAE,CAAC;AAC7C,SAAA;QACD,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACjC,KAAA;AACD,IAAA,OAAO,GAAG,CAAC;AACf,CAAC;AACD,UAAU,CAAC,SAAS,CAAC,aAAa,GAAG,aAAa;;ACrClD;;AAEG;;;;"}
@@ -1,11 +1,8 @@
1
- import { Subscription } from 'rxjs';
1
+ import { Observable, Subscription } from 'rxjs';
2
2
  declare module 'rxjs/internal/Observable' {
3
3
  interface Observable<T> {
4
4
  safeSubscribe: typeof safeSubscribe;
5
5
  }
6
6
  }
7
- export interface SafeSubscribable {
8
- _subscriptionFromSafeSubscribe$?: Subscription;
9
- ngOnDestroy(): void;
10
- }
11
- export declare function safeSubscribe<T>(target: SafeSubscribable, next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;
7
+ export declare function SafeSubscribe(destructorName?: string): ClassDecorator;
8
+ export declare function safeSubscribe<T>(this: Observable<T>, target: any, ...args: any): Subscription;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@badisi/ngx-safe-subscribe",
3
- "version": "3.0.1",
3
+ "version": "4.0.0",
4
4
  "description": "Easy way to automatically unsubscribe from RxJS observables in Angular components",
5
5
  "homepage": "https://github.com/badisi/ngx-safe-subscribe",
6
6
  "license": "MIT",
@@ -23,8 +23,8 @@
23
23
  "safesubscribe"
24
24
  ],
25
25
  "peerDependencies": {
26
- "@angular/core": ">=14.0.0",
27
- "rxjs": ">=6.0.0"
26
+ "@angular/core": ">= 14.0.0",
27
+ "rxjs": ">= 6.0.0"
28
28
  },
29
29
  "module": "fesm2015/badisi-ngx-safe-subscribe.mjs",
30
30
  "es2020": "fesm2020/badisi-ngx-safe-subscribe.mjs",