0

I have a large application built on Angular 7. I want to implement something which will unsubscribe the RxJs subscribers if developer has forgotten to unsubscribe in ngOnDestroy. This is to ensure no memory leak in application.

Is it possible with Guards? When the route changes, the Guard will check which component was last loaded and unsubscribe its subscribers?

user1312242
  • 339
  • 1
  • 3
  • 16
  • Does this answer your question? [Angular subscribe elegant](https://stackoverflow.com/questions/60223630/angular-subscribe-elegant) – ruth Nov 13 '20 at 05:31

4 Answers4

2

The easiest way i can think of is to use AsyncPipe on your application

This way angular will unsubscribe the observable when the component is destroy

Ref: https://angular.io/api/common/AsyncPipe

  • Thanks for your help. My application is huge it will be too much of work to change the subscribers and use AsyncPipe in HTML.Is there any way I can handle this in central location like a guard – user1312242 Nov 13 '20 at 10:14
1

Is it possible with Guards? When the route changes, the Guard will check which component was last loaded and unsubscribe its subscribers?

Unfortunately not out of the box. And the reason is it would be hard to discern which list of subscriptions you should be unsubscribing from. For example, it could be any of these :

subscriptions : Subscription[]
subscriptions : any[]
subscriptions : any

Or even subscriptions inside a service that you want to unsubscribe.

However there are libraries that will do some of this for you (For example : https://tutorialsforangular.com/2020/12/14/auto-unsubscribing-from-observables-on-ngdestroy/). Unfortunately they still require either an attribute on the component, and/or some conventions to be used that your subscriptions array is always named the same.

It may also be possible (Since it's open source), to take a dive into the code and be able to apply it to a routeguard etc.

MindingData
  • 11,924
  • 6
  • 49
  • 68
0
  1. Create new file auto-unsubscribe.ts containing exported class autoUnsubscribe

@Injectable()
export class autoUnsubscribe {
   subscriptions_: any[] = []
   get subscriptions(): Subscription[] {
    return this.subscriptions_
  }
   set subscriptions(v: any) {
    this.subscriptions_.push(v)
  }
   ngOnDestroy(): void {
    this.subscriptions.forEach((s) => {
      s.unsubscribe()
    })
  }
}
  1. On every component declaration add extends statement

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent extends autoUnsubscribe {
  title = 'app';
}
  1. Anywhere in your component class you can execute Subscribe like this:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent extends autoUnsubscribe implements OnInit{
  title = 'app';
  
  
  ngOnInit() {
    this.subscriptions=interval(2000).subscribe(console.log)
  }
}
  1. Extended class will take care of the rest!
Serhii
  • 51
  • 2
  • 7
0

The recommended way to do this is to use the takeUntil operator together with ngOnDestroy. For example:

    import { Component, OnDestroy, OnInit } from '@angular/core';
    // RxJs 6.x+ import paths
    import { filter, startWith, takeUntil } from 'rxjs/operators';
    import { Subject } from 'rxjs';
    import { BookService } from '../books.service';
    
    @Component({
        selector: 'app-books',
        templateUrl: './books.component.html'
    })
    export class BooksComponent implements OnDestroy, OnInit {
        private ngUnsubscribe = new Subject<void>();
    
        constructor(private booksService: BookService) { }
    
        ngOnInit() {
            this.booksService.getBooks()
                .pipe(
                   startWith([]),
                   filter(books => books.length > 0),
                   takeUntil(this.ngUnsubscribe)
                )
                .subscribe(books => console.log(books));
    
            this.booksService.getArchivedBooks()
                .pipe(takeUntil(this.ngUnsubscribe))
                .subscribe(archivedBooks => console.log(archivedBooks));
        }
    
        ngOnDestroy() {
            this.ngUnsubscribe.next();
            this.ngUnsubscribe.complete();
        }
    }

For more details, see this answer: Angular/RxJS When should I unsubscribe from `Subscription`

You might also be interested in these tslint rules. They will ensure some level of correct use of RxJS.

Patrick
  • 999
  • 1
  • 9
  • 21