8
votes

Right, so i have a header component (navbar) which contains the following template:

<ng-template [ngIf] = "authService.isAuthenticated()">
  <li>
    <a routerLink="Landing" class="navbar-brand" (click)="Logout()"><span class="xvrfont">Logout</span><i class="fa fa-sign-in" aria-hidden="true"></i></a>
  </li>
  <li>
    <a routerLink="Profile" class="navbar-brand"><span class="xvrfont">{{authService.getUsername()}}</span><i class="fa fa-user-circle" aria-hidden="true"></i></a>
  </li>
</ng-template>

This part of the nav should be visible when the user is authenticated. to find out it checks via authService.

To check if a user is authenticated, the following code is ran on every route change:

checkAuthenticated(){
   if  (localStorage.getItem('token') != null){ this.authenticated = true; }
   else { this.authenticated = false; }
   console.log(this.authenticated); // for Debugging. 
}

The NgIf statement calls this method:

public isAuthenticated(){
     return this.authenticated;
}

According to the logs, 'authenticated' is changing between true and false correctly, but the Ngif isn't responding to the changes somehow.

the header component.ts looks like this:

import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import {AuthService} from "../auth/auth.service";

@Component({
  selector: 'app-header',
  providers: [AuthService],
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css'],
  encapsulation: ViewEncapsulation.None
})
export class HeaderComponent implements OnInit {

  constructor(private authService: AuthService) { }

  ngOnInit() {
  }

  Logout(){
    this.authService.Logout();
  }

}

Any help would be appreciated. Thanks.

Edit:

auth.service.ts:

import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Router} from "@angular/router";
import 'rxjs/add/operator/map';

@Injectable()
export class AuthService {

  public apiroot = 'http://localhost:3100/';
  public loginResponseMessage = '';
  public registerResponseMessage = '';
  public authenticated = false;


  public constructor(private http: HttpClient,
                     private router: Router) {

  }



  SignUp(username: string, password: string) {
    const User = JSON.stringify({username: username, password: password});
    let response: any;
    this.http.post(this.apiroot + 'register', User, {headers: new HttpHeaders()
      .set('content-type', 'application/json; charset=utf-8')})
      .subscribe(res => {
        response = res;
        this.registerResponseMessage = response.message;
        console.log(this.registerResponseMessage);
      });
  }

  Login(username: string, password: string) {
    const User = JSON.stringify({username: username, password: password});
    let response: any;
    this.http.post(this.apiroot + 'authenticate', User, {headers: new HttpHeaders()
      .set('content-type', 'application/json; charset=utf-8')})
      .subscribe(res => {
        response = res;
        this.loginResponseMessage = response.message;
        if (response.token) {
          localStorage.setItem('token', response.token);
          this.authenticated = true;
          localStorage.setItem('user', response.username);
          this.router.navigate(['/']);
        }
        else{  /* Do Nothing */  }
      });
  }


  Logout(): void{
    this.authenticated = false;
    localStorage.removeItem('token');
    console.log(this.isAuthenticated());
    this.router.navigate(['/Landing']);
  }

  isAuthenticated(){
    return this.authenticated;
  }

  checkAuthenticated(){
    if  (localStorage.getItem('token') != null){ this.authenticated = true; }
    else { this.authenticated = false; }
    console.log(this.authenticated); // for Debugging.
  }



  getUsername(){
    var result = localStorage.getItem('user');
    return result;
  }
}
3
it should be *ngIf - Rahul Singh
can you post the AuthService class ? What is the return type from the method isAuthenticated() ? - Niladri
isAuthenticated() returns a boolean. i've added the AuthService to the post. - Daanv z
@Daanvz yes but it's default value is false, and it's updated asynchronously by subscribing to an Observable returned from this.http.post in Login method. - Niladri
@Daanvz can you try with my answer? - Niladri

3 Answers

9
votes

A good way is to share data through reactive coding with Observable.

In your service, create a BehaviorSubject and its Observable:

private _isAuthenticatedSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public isAuthenticatedObs: Observable<boolean> = _isAuthenticatedSubject.asObservable();

Each time you want to update your value, do a next on your subject:

_isAuthenticatedSubject.next(true); // authenticated
_isAuthenticatedSubject.next(false); // no more

Component side, just subscribe the observable to set the value locally for every subject changes:

this.authService.isAuthenticatedObs.subscribe(isAuth => this.isAuth = isAuth);

Or display value in your template with async pipe:

<ng-template *ngIf = "authService.isAuthenticatedObs | async">
6
votes

The problem is that you are providing the service at component level, that means, that all your components, that have the service added to providers array in component will have their own instance of service, so this is not a shared service at all. You want to have a singleton service, so only set the service in your providers array in your ngModule.

Also like mentioned by others, calling methods in template is a really bad idea, this method is called on each change detection, which is often, so it really hurts the performance of the app.

You could use Observables like suggested, or then just have a shared variable in your service. In the long run I'd suggest Observables, but depending on case just a shared variable is alright. Here's a sample for both: Passing data into "router-outlet" child components (angular 2)

3
votes

template should be

<ng-template *ngIf = "authService.isAuthenticated()">