Search code examples
angulartypescriptangular-ui-routerangular-lifecycle-hooks

Angular - Behavior of shared service subscribe


Using Angular I have a service to share some variable from different components. Like this:

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
@Injectable()
@Injectable({
  providedIn: "root"
})

/**
 * Service to manage all the global variables in order to use it in different components
 */
export class SharedService {

  // Global variable of the path 
  private rPathSource = new BehaviorSubject(""); // Set up the source
  currentRPath = this.rPathSource.asObservable(); // Make it Observable

 constructor() {}

  /**
   * Function to change the global path from a component
   * @param path string of the path of the R Folder for the result of the ML
   */
  changeRPath(path: any) {
    this.rPathSource.next(path);
  }
}

Then from a component I subscribe to it. Like this: Component 1

constructor(private shared: SharedService) {}

ngOnInit() {
    this.shared.currentRPath.subscribe(RPath => {
      this.currentRPath = RPath;
      // HERE I DO A GET REQUEST
    });
  }

And from another component I change the variable like this: Component 2

this.shared.changeRPath("");

I have a sidenav bar with some buttons and each button change the url and the component loaded with ng content.

<ng-content></ng-content>

When I press the button to redirect on the component 1 I susbcribe to the variable and the get request is done. Everything is fine.

The problem is when I press the button to redirect on the component 2 the shared variable changes and because I susbcribe on the component 1 it does the get request again. Indeed, the get request is in the callback of the subscribe.

But what is weird is that the component 1 is not load anymore because it is the component 2. It is not supposed to be destroyed when the component changes ?


Solution

  • You must not forget to unsubscribe to avoid memory leaks in these dangling subscriptions.

    Here are 2 ways to do that:

    1. Use takeUntil:

      export class MyComponent implements OnDestroy, OnInit {
        private readonly destroyed = new Subject<void>();
      
        constructor(private readonly shared: SharedService) {}
      
        ngOnInit() {
          this.shared.currentRPath.pipe(takeUntil(this.destroyed)).subscribe(/*...*/);
        }
      
        ngOnDestroy() {
          this.destroyed.next(undefined);
          this.destroyed.complete();
        }
      }
      
    2. Unsubscribe (useful when you have a single subscription):

      const mySubscription = this.shared.currentRPath.subscribe(/*...*/);
      mySubscription.unsubscribe(); // when done.