How to avoid multiple subscription in Angular 2 component

I think every Angular 2 developer has faced the situation when there are too many subscriptions in the component. If you unsubscribe manually - you waist too much time and litter the code. If you forgot to do that - your application uses too many resources.

To simulate this situation let's create the simple router, component and service:

@Injectable()
export class MyService {  
  name$ = new Subject();
}

Here we create 2 routes: test and home

export const ROUTES: Routes = [  
  { path: '',      component: HomeComponent },
  { path: 'test',  component: TestComponent }
];

and here we create the component

@Component({
  ...
})
export class TestComponent {  
  public subscribers: any = {};

  constructor(public myService: AppState){
  }
  ngAfterViewInit() {
  // The subscribe is made each time the view is uploaded
    this.subscribers.name = this.myService.name$
    .subscribe(name => {
      console.log(name)
    });

    this.myService.name$.next('New Name Created');
  }
}

In the added code we make the subscribe and type-out the name to console each time we reach subscribe.

If we will get across the route test several times we'll see that in console the name is typed-out many times. This is really not what we need (subscribe to Observable many times).

Let's change our component to solve the problem:

// The modified component
@Component({
  ...
})
export class TestComponent {  
  public subscribers: any = {};

  constructor(public myService: AppState){
  }

  ngAfterViewInit() {
    this.subscribers.name = this.myService.name$
    .subscribe(name => {
      console.log(name)
    });
    this.myService.name$.next('New Name Created');
  }
  // Cleanup just before Angular destroys the directive/component.
  // Unsubscribe observables and detach event handlers to avoid memory leaks.
  ngOnDestroy(){
    this.subscribers.name.unsubscribe();
  }
}

After these changes, we'll get what we expected in console - every time when getting to url '/test' the name will be typed-out once.

But what will happen if we have 20 subscribes in the component? It's really annoying - to unsubscribe from each Observables in every component.
To forget about unsubscribing - we can make a decorator. The main thing here is to save subscription in the variable this.subscribers, while the other job will be performed by decorator.

Let's get it started:

// creating the decorator DestroySubscribers
export function DestroySubscribers() {  
  return function (target: any) {
    // decorating the function ngOnDestroy
    target.prototype.ngOnDestroy = ngOnDestroyDecorator(target.prototype.ngOnDestroy);

    // decorator function
    function ngOnDestroyDecorator(f) {
      return function () {

        // saving the result of ngOnDestroy performance to the variable superData 
        let superData = f ? f.apply(this, arguments) : null;

        // unsubscribing
        for (let subscriberKey in this.subscribers) {
          let subscriber = this.subscribers[subscriberKey];
          if (subscriber instanceof Subscriber) {
            subscriber.unsubscribe();
          }
        }

        // returning the result of ngOnDestroy performance
        return superData;
      }
    }

    // returning the decorated class
    return target;
  }
}

Now we can remove the ngOnDestroy from the component and include the decorator that will be doing the same thing.

@Component({
  ...
})
//here we apply the decorator
@DestroySubscribers()
export class TestComponent {  
  public subscribers: any = {};

  constructor(public myService: AppState){
  }

  ngAfterViewInit() {
    this.subscribers.name = this.myService.name$
    .subscribe(name => {
      console.log(name)
    });
    this.myService.name$.next('New Name Created');
  }
}

The extended version of the decorator is available here: https://www.npmjs.com/package/ng2-destroy-subscribers

If your Angular2 application has many subscribers or you're tired of wasting time to make a lot of same code - you can use this decorator.