GregH
GregH

Reputation: 5459

Angular 2: have component auto detect variable change in a service

The issue I'm currently facing is that functionally, when a user logs in, my nav bar does not automatically update to show them the proper links. It will only update if I manually refresh the page which is not desired since this is a single page application. I can handle logging out OK since the logout button and functionality lives inside the component that controls the nav bar. However, logging in is controlled via an auth service and not visible to my component. I've tried making the isLoggedIn boolean public and then importing the component into the auth service and setting the value to true on login however this produces non-descript zone.js errors. Please see below- all help is appreciated.

app.component which controls my nav bar:

export class AppComponent implements OnInit{
    private isLoggedIn: boolean;

    constructor(private router: Router, private authenticationService: AuthenticationService) { }

    ngOnInit() {
        this.isLoggedIn = this.authenticationService.isLoggedIn();
    }

    logout() {
        this.isLoggedIn = false;
        this.authenticationService.logout();
        this.router.navigate([""]);
    }

    title = 'MYlestone';
}

and the app.component template:

<div class="site-container">
    <nav class="navbar navbar-toggleable-md">
        <div *ngIf="isLoggedIn">
            <span class="navbar-brand text-color">MYlestone</span>
        </div>
        <div *ngIf="!isLoggedIn">
            <span class="navbar-brand text-color" [routerLink]="['']" style="cursor:pointer">MYlestone</span>
        </div>
        <div>
            <div class="navbar-nav" *ngIf="isLoggedIn">
                <a class="nav-item nav-link" href="#" [routerLink]="['content']">My Content</a>
                <a class="nav-item nav-link" href="#" [routerLink]="['about']">About</a>
                <div class="ml-auto">
                    <a class="nav-item nav-link" href="#" (click)="logout()">Logout</a>
                </div>
            </div>
        </div>
    </nav>
    <div class="container-fluid text-color">
        <!-- let client side routing take over, see app.routing.ts -->
        <router-outlet></router-outlet>
    </div>
</div>

as you can see, isLoggedIn is set [properly] in the ngOnInit method and my component is updated appropriately when the logout button is clicked. What I'm struggling to figure out is how to update the isLoggedIn boolean in this component when a user logs in which occurs after this component's ngOnInit method has been executed. Just in case it's desired/needed, you can find the authentication.service below which is responsible for actually logging in a user:

@Injectable()
export class AuthenticationService {
    constructor(private http: Http) { }

    login(email: string, password: string) {
        return this.http.post('http://localhost:36000/api/accounts/authenticate', { email: email, password: password })
            .map((response: Response) => {
                let user = response.json();
                if (user && user.token) {
                    localStorage.setItem('currentUser', JSON.stringify(user));
                }
            });
    }

    logout() {
        localStorage.removeItem('currentUser');
    }

    isLoggedIn() {
        //check to see if token exists
        if (localStorage.getItem('currentUser')) {
            return true;
        }
        else {
            return false;
        }
    }
}

Upvotes: 2

Views: 1175

Answers (2)

Martin Parenteau
Martin Parenteau

Reputation: 73761

In your component class, you can make isLoggedIn a property that gets the current value from the service. Angular's change detection mechanism will access it when appropriate and update the rendered HTML.

public get isLoggedIn(): boolean {
    return this.authenticationService.isLoggedIn();
}

Upvotes: 2

joshrathke
joshrathke

Reputation: 7774

You should move the definition of isLoggedIn() from a function into an Observable that the component can subscribe to, and update if need be. This is just one possible solution though, there are a lot of ways to solve this issue.

Service.ts

private isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

// Sets up the isLoggedIn Observable
getLoggedInStatus(): Observable<boolean> {
    return this.isLoggedIn$.asObservable();
}

// Updates the Behavior Subject
updateLoggedInStatus(LoggedIn: boolean): void {
    this.isLoggedIn$.next(LoggedIn);
}

Component.ts

constructor(
    private _Service: Service
)

ngOnInit() {
    this._Service.getLoggedInStatus().subscribe(_isLoggedIn => {
        this.isLoggedIn = _isLoggedIn;
    }
}

Upvotes: 1

Related Questions