Search code examples
angularbehaviorsubject

Angular BehaviorSubject Subscribe. Call method in component after user login and logout


I have an angular layout with the header and footer. I want to update the loading of the navigation menus based on the user's role like readonly or analyst. I saw a post here on SO and copied the BehaviorSubject code for the header component to subscribe to a boolean change in a service. This works but I only got the one login button to change to the sign out button. In my header component ts code, I have a method that will load the menus based on the user's role. It take a list of the menu path and load only the menu items that is appropriate for the user's role. So, in addition to using *ngIf to changing hiding base on the subscribed boolean value change, is there a way to call a method from the subscribed change when the header component is notified by the emitted change?

Any help is appreciated. Thanks.

Here is my header component ts code so far:

export const ROUTES: RouteInfo[] = [
  { path: '/Home', title: 'Home', icon: '', class: '', role: 'all'},

  // admin
  { path: '/change-role', title: 'Change Role', icon: '', class: '', role: 'admin'},
  { path: '/users', title: 'User', icon: '', class: '', role: 'admin'},
  { path: '/products', title: 'products', icon: '', class: '', role: 'admin'},
  { path: '/admin', title: 'Admin', icon: '', class: '', role: 'admin'},
];

  constructor(private dataSharingService: DataSharingService, private loginService: LoginService, private router : Router)  { 
      this.dataSharingService.isUserLoggedIn.subscribe( value => { this.isUserLoggedIn = value })
  }

  ngOnInit() {
    //this.getLoginStatus();
    this.loadMenus();

  }

  loadMenus()
  {
      var role = this.loginService.getUserRole();
      if (role == 'admin') {
        this.menuItems = ROUTES.filter(menuItem => menuItem.role == 'admin');
      }
      else if (role == 'analyst' || role == 'reviewer') {
        this.menuItems = ROUTES.filter(menuItem => menuItem.role == 'reviewer');
      }
      else if (role == ' readOnly') {
        this.menuItems = ROUTES.filter(menuItem => menuItem.role == 'readOnly');
      }

    }
  } 

Solution

  • I guess this is what you want to do.

    this.subscription = this.dataSharingService.isUserLoggedIn.subscribe(value => {
      this.isUserLoggedIn = value;
      this.loadMenus();
    });
    
    ngOnDestroy() {
      this.subscription.unsubscribe()
    }
    

    The unsubscribe is very important here to prevent a memory leak:

    • The service is a singleton (one instance for the whole app lifetime)
    • The BehaviorSubject lasts forever too because it's in the service
    • The BehaviorSubject holds a subscription in the form of a callback. That callback has a reference to this
    • this is the current instance of the component class

    In short, if you don't unsubscribe in the ngOnDestroy hook, the instance of the component's class will remain forever, taking memory even if the actual component has been destroyed in the UI.