Angular 2: Displaying icons in navbar on authentication

五迷三道 提交于 2019-12-23 04:45:51

问题


My component structure looks roughly like this. My app component has a nav bar and router outlet. The nav bar has logo, some generic links and some specific links to be shown on user login and authentication only. The router outlet loads the home component or the wall component based on the routing url. The home component contains the login component which contains the customary user id, password and submit button. On submit, and upon successful login, the login component emits an event. Now, how do I catch that event in the home component (parent)?

If I were to use the home selector directly under app, I could catch the event, bubble it up to app and then make the hidden links in the nav bar visible.

I am unaware how to catch the event emitted by login component in home component since it is loaded in the router output.

<!-- app.html -->
<div>
  <nav>
    <!-- Product logo -->
    <!-- Some generic links -->
    <!-- some hidden icons to be shown on authentication -->
  </nav>
  <router-outlet></router-outlet>
</div>

<!-- home.html -->
<div>
  <login></login>
</div>

<!-- login.html -->
<div>
  <!-- user name and password -->
  <!-- submit button - the associated ts file raises an event on successful login -->
</div>

<!-- wall.html -->
<div>
  <!-- Content to be displayed on authentication -->
</div>

Thanks, Shilpa


回答1:


I figured out the issue, although I still don't have a solution. Turns out the method being called on map cannot access the class level objects. If I call emit the event on authentication, it works perfectly fine. I tried passing the event emitter object to the mapped method but that isn't helping either. How do I pass the object to mapped method scope?

The code in my service looks like this:

authenticate(authRequest: login): Observable<user> {
        let url: string = this.apiUrl + AppSettings.LOGIN_SERVICE;
        let headers = new Headers({
            'Content-Type': AppSettings.CONTENT_TYPE_HEADER
        });

        let options = new RequestOptions({ headers: headers });

        return this._http.post(url, authRequest, options) // ...using post request
            .map(this.authenticated)
            .catch(this.handleError);

        //.catch(this.handleError);
    }

    private authenticated(res: Response) {
        let body = res.json();
        if (body.StatusCode === 200) {
            localStorage.setItem('auth_token', res.headers.get("access-token"));
          //This is the event declared at the class level.
          //The following line of code is the one giving the error - cannot execute emit on  undefined
            this.authEvent.emit(true);
            return body.Data || {};
        }
        else {
            return {};
        }
    }



回答2:


Sorry for delay, here is how I do it:

auth.service.ts

export class AuthService {

    authChanged: EventEmitter<any> = new EventEmitter();

    postLogin(f: ILoginForm) {
      return this.http.post('/login', f)
      .map(res => res.json())
      .subscribe(data => this._checkLoginResponse(data));
    }

    /**
     * Check Login Result
     * @param data
     */
    private _checkLoginResponse(data: any) {
      // If Successful Login
      if (data.data && data.meta && data.meta.token) {

        // Set User & Token
        localStorage.setItem('inctoken', data.meta.token);
        localStorage.setItem('incuser', JSON.stringify(data.data));

        // Emit Auth & User Events
        this.authChanged.emit(this.getUser());

        // Show OK Login Flash Message
        this.flash.success('You have been logged in successfully.');

        // Navigate Home
        this.injector.get(Router).navigate(['/']);
      }
    }

    /**
     * Logout of Interface
     * @returns boolean
     */
    logout(withMsg = false): boolean {
      // # Remove Token & User from LS
      localStorage.removeItem('inctoken');
      localStorage.removeItem('incuser');

      // Emit Auth Events
      this.authChanged.emit(false);

      // Show Flash Message
      if (withMsg)
        this.flash.info('You have been logged out.', 'OK.');

      // Redirect to Login Page
      this.injector.get(Router).navigate(['/login']);
      return true;
    }
}

Then any component can inject AuthService and know when the authedUser changes, logs out, etc. Take my Navbar for example:

navbar.component.ts

export class NavbarComponent {

  /**
   * Authed User
   */
  authedUser: IUser;


  constructor(private authService: AuthService) {
    this.authService.authChanged
      .subscribe((user?: IUser) => {
        // user will be false if logged out
        // or user object if logged in. 
        this.authedUser = user;
      });
    }
}

Then in my navbar template I can choose to display things based on auth status:

<nav class="navbar">

  <!-- Truncated -->
  <ul class="menu-item"
      *ngIf="authedUser">
      <li><img [src]="authedUser.avatar"></li>
      <!-- Whatever -->
  </ul>

  <ul class="menu-item"
      *ngIf="!authedUser">
      <li><a [routerLink]="['/login']">Login</a></li>
      <!-- Whatever -->
  </ul>

</nav>  

Obviously a lot of these classes are truncated for brevity.

If you're curious about how the token gets sent in the header, here is a light example of the HttpClient class I created (truncated for brevity):

http.service.ts

export class HttpClient {

  constructor(private http: Http) {
    // ...
  }

  /**
   * Get
   * @param url
   * @returns {Observable<any>}
   */
  get(url): Observable<any> {

    // Create New Headers
    let headers = new Headers();

    // Set Authorization Header
    this._createAuthorizationHeader(headers);

    // Create Observable to Return to Calling Service.
    // We Dont Just Return the HTTP Observable Because
    // We Need to Subscribe Here to Catch The Errors That
    // May Be Thrown, Otherwise, Every Service Would Need
    // To Call The Handle Errors Method
    return Observable.create((observer) => {

      // Fire Http GET Request, Subscribe & Catch Errors
      return this.http.get(this.baseUrl + url, {
        headers: headers
      }).subscribe(
        data => observer.next(data),    // Emit Data Returned
        err => this.handleError(err),   // Catch Errors
        () => observer.complete()       // Emit Completed
      );
    });
  }

  /**
   * Create Authorization Header
   * @param {Headers} headers
   */
  private _createAuthorizationHeader(headers: Headers) {

    // If We Have A Token, Append It.  The
    // API Server Will Determine Its Validity
    if (localStorage.getItem('inctoken')) {
      headers.append('Authorization', 'Bearer: ' + localStorage.getItem('inctoken'));
    }
  }
}

Then in my other components I can just inject my HttpClient class and use it to have token automatically placed in headers, I.E.

some.component.ts

export class SomeComponent {

  constructor(private http: HttpClient) {
    // ...
  }

  private _getSomeData() {
    return this.get('/someurl')
      .map(res => res.json();
  }
}



回答3:


Chanced upon this solution --> http://plnkr.co/edit/KfcdDi?p=info

//alert.service.ts

import { Injectable } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class AlertService {
    private subject = new Subject<any>();
    private keepAfterNavigationChange = false;

    constructor(private router: Router) {
        // clear alert message on route change
        router.events.subscribe(event => {
            if (event instanceof NavigationStart) {
                if (this.keepAfterNavigationChange) {
                    // only keep for a single location change
                    this.keepAfterNavigationChange = false;
                } else {
                    // clear alert
                    this.subject.next();
                }
            }
        });
    }

    success(message: string, keepAfterNavigationChange = false) {
        this.keepAfterNavigationChange = keepAfterNavigationChange;
        this.subject.next({ type: 'success', text: message });
    }

    error(message: string, keepAfterNavigationChange = false) {
        this.keepAfterNavigationChange = keepAfterNavigationChange;
        this.subject.next({ type: 'error', text: message });
    }

    getMessage(): Observable<any> {
        return this.subject.asObservable();
    }
}

//login.component

private loggedIn(user1: user) {
        this.currentUser = user1;
        this._alertService.alert("login", true);
     
    }

//app.component

ngOnInit(): void {
        this.authenticated = this._authService.isLoggedIn();
        this._alertService.getMessage().subscribe(data => this.setData(data));
    }

private setData(data: any) {
        if (!this.authenticated) {
            if (data && (data.type === 'login') && data.success === true) {
                this.authenticated = true;
            }
            else {
                this.authenticated = false;
            }
        }
    }

<!-- app.html -->
    <nav class="navbar navbar-color">
        <div class="container-fluid" id="nav_center">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed nav-expand-button" 
                        data-toggle="collapse" data-target="#navbar-collapse1" aria-expanded="false"
                        *ngIf="authenticated">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <button type="button" class="nav-features nav-expand-button"
                        (click)="isCollapsed = !isCollapsed" *ngIf="authenticated">
                    <span class="sr-only">Navigate features</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">Outili</a>
            </div>
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="navbar-collapse1" *ngIf="authenticated">
                <!--*ngIf="showNotification">-->
                <ul class="nav navbar-nav navbar-right">
                    <li class="navbar-icons">
                        <a href="#" class="navbar-a">
                            <span class="glyphicon glyphicon-inbox navbar-icons"></span>
                        </a>
                    </li>
                    <li class="dropdown navbar-icons">
                        <a href="#" class="dropdown-toggle navbar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                            <span class="glyphicon glyphicon-user navbar-icons"></span>
                            <span class="caret navbar-icons"></span>
                        </a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Profile</a></li>
                            <li><a href="#">Settings</a></li>
                            <li role="separator" class="divider"></li>
                            <li (click)="logout()"><button type="button" class="btn">Logout</button></li>
                        </ul>
                    </li>
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>

The alert service here does exactly what I am looking for.



来源:https://stackoverflow.com/questions/40264705/angular-2-displaying-icons-in-navbar-on-authentication

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!