5

I am trying to use a RxJS BehaviorSubject that contains a boolean representing whether or not a user is connected/logged in the application.

Here is the component that subscribes to the new BehaviorSubject value i.e. true when the user has authenticated:

import {Component, OnInit} from '@angular/core';
import {CollapseDirective} from 'ng2-bootstrap';
import {PublicNavbarComponent} from './public.navbar.component';
import {PrivateNavbarComponent} from './private.navbar.component';
import {SessionService} from '../../session/session.service';

@Component({
    selector: 'navbar',
    templateUrl: 'app/shared/components/navbar.component.html',
    providers: [SessionService],
    directives: [PublicNavbarComponent, PrivateNavbarComponent, CollapseDirective]
})
export class NavbarComponent implements OnInit {

    constructor(private sessionService:SessionService) {
    }

    ngOnInit() {
        this.sessionService.authenticated$.subscribe({
            next: (value)=> this.isAuthenticated = value
        });
    }

    isAuthenticated:boolean = false;

    isCollapsed:boolean = true;
}

Here is the class/service containing the BehaviorSubject:

import {Injectable} from '@angular/core';
import {Http, Headers, RequestOptions} from '@angular/http';
import {Credentials} from '../shared/models/credentials.model';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import 'rxjs/Rx';

@Injectable()
export class SessionService {

    authenticated$:BehaviorSubject<boolean> = new BehaviorSubject(false);
    currentUserAccount;

    constructor(private http:Http) {
    }

    signin(credentials:Credentials) {
        let headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
        let options = new RequestOptions({headers: headers});
        this.http.post('/api/signin', 'username=' + credentials.username + '&password=' + credentials.password, options)
            .subscribe(response=>
                this.setPersonalInfo(response.headers.get('x-auth-token'))
            );
    }

    setPersonalInfo(sessionToken) {
        localStorage.setItem('authenticated', 'true');
        localStorage.setItem('sessionToken', sessionToken);
        this.authenticated$.next(true);//next() from false to true
        this.http.get('/api/utils/current-useraccount')
            .subscribe(param => this.currentUserAccount = param);
    }
}

However this does not have the expected behavior: it seems that authenticated$ from session service is only true within setPersonalInfo function and isAuthenticated from NavbarComponent isn't notified at all when this.authenticated$.next(true) is invoked.

balteo
  • 23,602
  • 63
  • 219
  • 412

2 Answers2

7

It seems like you have multiple instances of SessionService. e.g. you provided it more than once and the instance that you call signin in is not the same instance as the one you injected in NavbarComponent.

It depends on your project design how you provide your services. Usually session services are singletons.
I recommend removing providers: [SessionService], from NavbarComponent.

Abdulrahman Alsoghayer
  • 16,462
  • 7
  • 51
  • 56
1

I usually provide SessionService at bootstrap (and not with providers: []) to make sure you share one global SessionService instance throughout the entire app.

bootstrap(AppComponent,
            [SessionService]);
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Picci
  • 16,775
  • 13
  • 70
  • 113
  • I like `bootstrap()` as well because it makes it more obvious that the providers are supposed to be global but according to the style guide, global providers should not be added to `bootstrap()` but instead to the root component. There are no technical reasons for this. They just consider it more maintainable. – Günter Zöchbauer May 11 '16 at 05:56