angularobservable

Angular 2 duplicated subscription in component


Is there a way to avoid duplicated subscription on a behaviorSubject in a component other than unsubscribing to it in ngOnDestroy ? So far, it is the only way I have found to avoid duplicated subscription when navigating back and forth on a component in which I create a subscription on an observable.

Example:

A user service

@Injectable()
export class UserService {

  constructor(private http: Http) {
    this.setCurrentUser();
  }

  private currentUser$ = new BehaviorSubject<User>(null);

  public getCurrentUser(): Observable<User> {
    return this.currentUser$.asObservable();
  }
  public setCurrentUser(): void {
    this.getLoggedUser(); // 
  }


  private getLoggedUser(): void {

    let getCurrentUserUrl = 'http://127.0.0.1:8000/users/current/'

    let headers = new Headers({
      'Content-Type': 'application/json'
    });
    let options = new RequestOptions({
      headers: headers
    });
    options.withCredentials = true;

    this.http.get(getCurrentUserUrl, options)
      .map(this.toUser)
      .catch(this.handleError)
      .subscribe(
        user => this.currentUser$.next(user),
        error => console.log("Error subscribing to currentUser: " + error)
      );

  }

  private toUser(res: Response): User {
    let body = res.json();
    return body || { };
  }

}

And a component subscribing to the observable from the user service...

export class AppComponent implements OnInit, OnDestroy {

  currentUserSubscription:any;

  constructor(
    private userService:UserService,
    private authentificationService:AuthenticationService
  ) {}

  user:User;

  ngOnInit() {
    this.currentUserSubscription =  this.userService.getCurrentUser().subscribe(
      data => {
        this.user = data;
        console.log('Main : ', this.user);
      }
    );
  }

  ngOnDestroy() {
    // I want to avoid writing this for every subscription
    this.currentUserSubscription.unsubscribe();
  }

}

If I navigate mulitple time to the component, it get created and destroyed multiple time. The subscription is created every time with the component initialization and has to be destroyed with the component. If not, it will be duplicated on the next component initialization...

Is there a way that avoid cleaning subscription in ngOnDestroy?


Solution

  • If you want to subscribe only once you will need to use the async pipe on the template, async pipe will manage automatically the unsubscription. If you like this approach you need to compose your application with smart component and presentation component. Check this answer

    Another way to unsubscribe it's to create a Subject so the subscription will complete until the subject emits a value. You should always unsubscribe or you will have memory leaks.

    export class AppComponent implements OnInit, OnDestroy {
    
      currentUserSubscription:any;
    
      constructor(
        private userService:UserService,
        private authentificationService:AuthenticationService,
        private _destroy : Subject() = new Subject();
      ) {}
    
      user:User;
    
      ngOnInit() {
        this.currentUserSubscription =  this.userService.getCurrentUser()
        .takeUntil(this._destroy)
        .subscribe(
          data => {
            this.user = data;
            console.log('Main : ', this.user);
          }
        );
      }
    
      ngOnDestroy() {
        this._destroy.next();
        this._destroy.unsubscribe();
      }
    
    }