{"id":23854770,"url":"https://github.com/piotrekn/ng-rxjs-safe-subscribe","last_synced_at":"2025-09-08T01:32:24.101Z","repository":{"id":37998845,"uuid":"344945557","full_name":"piotrekn/ng-rxjs-safe-subscribe","owner":"piotrekn","description":"Implementation of Angular's repeatable OnDestroy pattern","archived":false,"fork":false,"pushed_at":"2024-12-22T12:20:56.000Z","size":600,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-22T12:43:29.244Z","etag":null,"topics":["angular","rxjs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/piotrekn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-05T22:04:06.000Z","updated_at":"2024-12-22T12:21:00.000Z","dependencies_parsed_at":"2024-05-27T23:14:10.497Z","dependency_job_id":null,"html_url":"https://github.com/piotrekn/ng-rxjs-safe-subscribe","commit_stats":{"total_commits":31,"total_committers":2,"mean_commits":15.5,"dds":0.4838709677419355,"last_synced_commit":"0153231a0a11ab830149745084d16f7d2e91647d"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrekn%2Fng-rxjs-safe-subscribe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrekn%2Fng-rxjs-safe-subscribe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrekn%2Fng-rxjs-safe-subscribe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrekn%2Fng-rxjs-safe-subscribe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/piotrekn","download_url":"https://codeload.github.com/piotrekn/ng-rxjs-safe-subscribe/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232271882,"owners_count":18497768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","rxjs"],"created_at":"2025-01-03T00:01:38.470Z","updated_at":"2025-01-03T00:03:24.022Z","avatar_url":"https://github.com/piotrekn.png","language":"TypeScript","funding_links":[],"categories":["Underlying Technologies"],"sub_categories":["RxJS"],"readme":"\u003ch1 align=\"center\"\u003eng-rxjs-safe-subscribe\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\nImplementation of \u003ca href=\"https://angular.io/\"\u003eAngular's\u003c/a\u003e repeatable OnDestroy pattern.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://badge.fury.io/js/ng-rxjs-safe-subscribe\"\u003e\u003cimg src=\"https://badge.fury.io/js/ng-rxjs-safe-subscribe.svg\" alt=\"npm version\" \u003e\u003c/a\u003e\n    \u003ca href=\"https://npmjs.org/ng-rxjs-safe-subscribe\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/ng-rxjs-safe-subscribe.svg\" alt=\"npm downloads\" \u003e\u003c/a\u003e\n    \u003ca href=\"https://wallabyjs.com/oss/\"\u003e\u003cimg src=\"https://img.shields.io/badge/wallaby.js-powered-blue.svg?style=flat\u0026logo=github\" alt=\"wallaby\" \u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Why do I need it?\n\nBecause of the DRY principle. Instead of reimplementing the pattern in every component:\n\n```typescript\nexport class MyComponent implements OnDestroy {\n  private booksSubscription: Subscription;\n\n  getBooks(): void {\n    this.booksSubscription = this.booksService.getBooks().subscribe((books) =\u003e\n      (...)\n    );\n  }\n\n  ngOnDestroy(): void {\n    this.booksSubscription.unsubscribe();\n  }\n}\n```\n\nsimplify the code and just subscribe safely:\n\n```typescript\nexport class MyComponent extends RxjsOnDestroy {\n  getBooks(): void {\n    this.booksService.getBooks().subscribeSafely(this, (books) =\u003e\n      (...)\n    );\n  }\n}\n```\n\nOne of the most common mistakes made with RxJS is subscribing to an Observable in a fire-and-forget manner.\n\nA rule of thumb is that a subscriber should unsubscribe when no longer using an observable. If there is no explicit unsubscribe, then those books will be pushed to subscribe function infinitely. While it may not be the case for some observable sources, it can silently become an issue and cause a \u003ci\u003eleaks\u003c/i\u003e leading to unwanted behaviors.\n\nThere are a few ways to deal with unsubscribing. A direct:\n\n1. calling unsubscribe() directly on subscription.\n\n... or more declarative one using \u003ca href=\"https://rxjs-dev.firebaseapp.com/guide/overview/\"\u003eRxJs\u003c/a\u003e:\n\n2. using operator: takeUntil, takeWhile (declarative unsubscribe),\n3. taking a finite number of values: first, take(n) - it'll unsubscribe after n emits, and only then,\n4. async pipe in HTML template - takes care of the issue automagically.\n\nPackage `ng-rxjs-safe-subscribe` provides a ready-to-use solution for every Angular project.\n\n# Installation\n\nInstall `ng-rxjs-safe-subscribe` from `npm`:\n\n```bash\nnpm install ng-rxjs-safe-subscribe\n```\n\nImport an abstract class:\n\n```typescript\nimport { RxjsOnDestroy } from 'ng-rxjs-safe-subscribe';\n```\n\nExtend the class with `RxjsOnDestroy` that implements the `OnDestroy` hook.\n\n```typescript\nexport class AppComponent extends RxjsOnDestroy\n```\n\nFinally, use one of the following approaches to subscribe in code using `Observable.subscribeSafely` or `Observable.subscribeUntil` function.\n\n## How to execute custom logic at `ngOnDestroy`\n\nConsider passing an arrow function with custom destroy logic to the constructor:\n\n```typescript\n    constructor() {\n        super(() =\u003e this.customDestroy());\n    }\n```\n\nThe `ngOnDestroy` function can be also easily overridden, but be sure to \u003cu\u003ealways call the base function\u003c/u\u003e for `RxjsOnDestroy` to unsubscribe properly:\n\n```typescript\n    override ngOnDestroy(){\n        super.ngOnDestroy();\n\n        ...\n    }\n```\n\nTypescript can help you avoid mistaken overrides with [noImplicitOverride](https://www.typescriptlang.org/tsconfig#noImplicitOverride) rule. It is HIGHLY RECOMMENDED to enable that.\n\n## 1. Unsubscribe with a sink\n\nSubscribe safely, pass an object which extends RxjsOnDestroy abstract class:\n\n```typescript\nthis.users$.subscribeSafely(rxjsOnDestroyInstance, (x) =\u003e console.log(x));\n```\n\nFull example:\n\n```typescript\nimport { Component } from '@angular/core';\nimport { RxjsOnDestroy } from 'ng-rxjs-safe-subscribe';\nimport { Observable } from 'rxjs';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent extends RxjsOnDestroy {\n  users$: Observable\u003cUser[]\u003e;\n\n  constructor(private userService: UserService) {\n    super();\n\n    this.users$ = this.userService.getUsers();\n    this.users$.subscribeSafely(this, (x) =\u003e console.log(x));\n  }\n}\n```\n\n## 2. Declarative unsubscribe\n\nYou may pass an observable instance that triggers unsubscribe by passing a value and completion:\n\n```typescript\nthis.users$.subscribeUntil(this.destroy$, (x) =\u003e console.log(x));\n```\n\nThe example uses `this.destroy$` of `RxjsOnDestroy` class.\n\nFull example:\n\n```typescript\nimport { Component } from '@angular/core';\nimport { RxjsOnDestroy } from 'ng-rxjs-safe-subscribe';\nimport { Observable, fromEvent, merge } from 'rxjs';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent extends RxjsOnDestroy {\n  users$: Observable\u003cUser[]\u003e;\n\n  constructor(private userService: UserService) {\n    super();\n\n    this.users$ = this.userService.getUsers();\n\n    const cancelBtn = this.element.querySelector('.cancel-button');\n    const cancel$ = fromEvent(cancelBtn, 'click');\n    const stop$ = merge(cancel$, this.destroy$)\n\n    // will stop when button clicked or component destroyed\n    this.users$.subscribeUntil(stop$, (x) =\u003e console.log(x));\n\n    // will stop when component destroyed\n    this.users$.subscribeUntil(this.destroy$, (x) =\u003e console.log(x));\n }\n}\n```\n\nYou can now use stop to kill the subscription in the moment of your choosing, but remember to always unsubscribe on object destruction.\n\nRead up [Ben Lesh's article](https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87) for more on this topic.\n\n# What about Signals?\n\nSignals can be an answer to this kind of issue. If you just want to provide a value from rxjs Observable, consider using Signals.\n\n# Compatibility\n\nThe only two dependencies are [Angular](https://angular.io) and [RxJS](https://rxjs-dev.firebaseapp.com/guide/overview).\nHere is the versions compatibility list:\n\n| ng-rxjs-safe-subscribe | Angular | RxJS  |\n| ---------------------- | ------- | ----- |\n| 18.x.x                 | 18.x.x  | 7.x.x |\n| 17.x.x                 | 17.x.x  | 7.x.x |\n| 16.x.x                 | 16.x.x  | 7.x.x |\n| 15.x.x                 | 15.x.x  | 7.x.x |\n| 14.x.x                 | 14.x.x  | 7.x.x |\n| 13.x.x                 | 13.x.x  | 7.x.x |\n| 12.x.x                 | 12.x.x  | 6.x.x |\n| 11.x.x                 | 11.x.x  | 6.x.x |\n| 10.x.x                 | 10.x.x  | 6.x.x |\n\nThe package should work with every version of Angular, as long as the RxJS version is matching yours.\n\n# License\n\n[ISC](https://opensource.org/licenses/ISC)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrekn%2Fng-rxjs-safe-subscribe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpiotrekn%2Fng-rxjs-safe-subscribe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrekn%2Fng-rxjs-safe-subscribe/lists"}