Using Angular 2 to showcase icons in the navbar post authentication

The structure of my components is as follows: The app component contains a navigation bar and router outlet. The navigation bar includes a logo, generic links, and specific links that are only shown after user login and authentication. The router outlet determines whether to load the home component or the wall component based on the URL routing. Inside the home component is the login component, which consists of fields for the user ID, password, and submit button. Upon successful login, the login component emits an event. My challenge is figuring out how to catch this event in the home component (parent).

If I were to place the home selector directly under the app component, I could easily capture the event, pass it up to the app component, and then make the hidden links in the navigation bar visible.

I am unsure how to handle the event emitted by the login component within the 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>

Thank you, Shilpa

Answer №1

After identifying the issue, I'm still searching for a solution. It appears that the method called on map is unable to access the class-level objects. When I emit the event on authentication, everything works smoothly. I even tried passing the event emitter object to the mapped method but it didn't solve the problem. How can I pass the object to the mapped method scope?

Below is the snippet of code from my service:

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"));
          //The following line of code is causing an error - cannot execute emit on undefined
            this.authEvent.emit(true); //This event is declared at the class level.
            return body.Data || {};
        }
        else {
            return {};
        }
    }

Answer №2

Apologies for the delay, here is my approach:

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) {
      // Verifying Successful Login
      if (data.data && data.meta && data.meta.token) {

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

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

        // Displaying Login Success Message
        this.flash.success('You have been logged in successfully.');

        // Redirect to Home Page
        this.injector.get(Router).navigate(['/']);
      }
    }

    /**
     * Log out from 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.');

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

Any component can then utilize AuthService to track changes in authedUser, logouts, etc. For instance, take a look at my Navbar component:

navbar.component.ts

export class NavbarComponent {

  /**
   * Authenticated 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;
      });
    }
}

This allows me to display different content in the navbar based on the authentication 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>  

Note that many classes are shortened for brevity purposes.

If you are interested in how the token is sent in the header, here is an abridged example of the HttpClient class I created:

http.service.ts

export class HttpClient {

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

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

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

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

    // Creating Observable for Service Return.
    return Observable.create((observer) => {

      // Sending HTTP GET Request, Subscribe & Handle 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 Completion
      );
    });
  }

  /**
   * 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'));
    }
  }
}

In other components, I can simply inject my HttpClient class and utilize it with the token automatically included in the headers, like so:

some.component.ts

export class SomeComponent {

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

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

Answer №3

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

//notification.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 NotificationService {
    private subject = new Subject<any>();
    private keepAfterNavigationChange = false;

    constructor(private router: Router) {
        // clear notification 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 notification
                    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._notificationService.notify("login", true);
     
    }

//app.component

ngOnInit(): void {
        this.authenticated = this._authService.isLoggedIn();
        this._notificationService.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 notification service provided here fulfills my requirements perfectly.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When I try to ng build my Angular 11 application, why are the type definitions for 'Iterable', 'Set', 'Map', and other types missing?

As someone new to Angular and the node ecosystem, I'm facing a challenge that may be due to a simple oversight. The issue at hand: In my Angular 11 project within Visual Studio 2019, configured with Typescript 4, attempting to run the project throug ...

Angular2, multi-functional overlay element that can be integrated with all components throughout the application

These are the two components I have: overlay @Component({ selector: 'overlay', template: '<div class="check"><ng-content></ng-content></div>' }) export class Overlay { save(params) { //bunch ...

What is the correct way to specify an image path for a background URL?

Currently, I am setting a background image for the hr tag using the following code: <hr style="height:6px;background: url(http://ibrahimjabbari.com/english/images/hr-11.png) repeat-x 0 0;border: 0;margin:0px!important"> However, I have now saved th ...

The ngx-incredible-scrollbar is malfunctioning

I encountered an issue when trying to include the "perfect-scrollbar" library in my project. I followed the installation and inclusion steps provided below: However, upon running the project, I'm facing the following error: Error: Unexpected token & ...

Typescript: The type 'Observable<{}>' cannot be assigned to the type 'Observable'

I'm encountering an issue with the Observable type, any thoughts on how to resolve it? import { PostModel } from '../model/postModel'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable&ap ...

Can anyone suggest a more efficient method for validating checkbox selection in Angular?

I am working with an angular material stepper, where I need to validate user selections at each step before allowing them to proceed. The first step displays a list of 'deliveries' for the user to choose from, and I want to ensure that at least o ...

Ways to incorporate a custom JavaScript function that is activated by an external server system?

I'm currently exploring a JavaScript widget that needs to operate within specific constraints: The widget initiates a request to a third-party server using a callback URL The third-party server pings the callback URL after a set period, triggering a ...

What steps do I need to take to update Oceania to Australia on Google Maps?

While incorporating the Google Maps API into Angular, an issue arises when zooming out completely: https://i.stack.imgur.com/oZRut.png The label is displaying "Oceania" instead of "Australia". Is there a feasible method to modify this discrepancy? ...

Instructions for implementing personalized horizontal and vertical scrolling within Angular 9

I am currently working on an angular application where users can upload files, and I display the contents of the file on the user interface. These files may be quite long, so I would need vertical scrolling to navigate through them easily. Additionally, fo ...

The inclusion of an XSRF-TOKEN using an HTTP Interceptor results in a 400 Error Request

Implementing XSRF-TOKEN to enhance security and prevent XSRF attacks as I pass a JWT through a cookie for my Angular application. Below is the structure of my HttpInterceptor. intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEv ...

Generating a fresh object from an existing object by incorporating updated values using Angular and Ionic version 3

Currently, I am actively working on an Ionic 3 project that utilizes Angular framework. Within my project, I have a JSON object called 'person' which consists of data fields such as name, job, and home. One feature in my app is an Ionic toggle b ...

Getting a 404 response for incorrect API URLs in ASP.NET Core and single-page applications

In order to properly handle incorrect API calls on the client side with Angular 5, I need to ensure that a 404 error is returned. Currently, the backend is returning a status code of 200 along with index.html, resulting in a JSON parse error on the fronten ...

When I close all of my tabs or the browser, I aim to clear the local storage

Could you recommend any strategies for clearing local storage upon closing the last tab or browser? I have attempted to use local storage and session storage to keep track of open and closed sessions in an array stored in local storage. However, this meth ...

Setting a specific time zone as the default in Flatpickr, rather than relying on the system's time zone, can be

Flatpickr relies on the Date object internally, which defaults to using the local time of the computer. I am currently using Flatpickr version 4.6.6 Is there a method to specify a specific time zone for flatpickr? ...

Troubleshooting Angular 6: Issues with Route Guards not functioning as expected

Striving to enhance frontend security by restricting access to specific IDs. The goal is to redirect anyone trying to access routes other than /login/:id to a page-not-found error message if not already logged in, but encountering some issues. Below are t ...

"Exploring the process of implementing a fixed method POST in Angular 5

When developing an application for Portal, I encountered an issue where a newly created role is not displayed without refreshing the browser. How can I ensure that the added element is directly displayed in the table without needing to refresh the browser? ...

Using the <template> syntax to apply ngclass or [class.classname]

I am familiar with the syntax for ngFor, ngIf, and ngSwitch using templates. Can someone provide guidance on how to utilize ngClass or [class.className] with template syntax in Angular 2? Can anyone explain how to implement classes using template syntax i ...

Leveraging data from a service in Angualr 5 within createServerRenderer

I am currently utilizing the .Net Core Angular CLI based template available at this link. When it comes to server side rendering, this template generates a crucial file named main.server. import 'zone.js/dist/zone-node'; import 'reflect-me ...

What are the steps to creating a service that returns Observables?

I've been working on a project that utilizes Google Maps Places Autocomplete API to fetch a list of objects. The service is simple; I'm using the Maps JavaScript API to pass a string and receive a list of matches in return. However, the challeng ...

What steps can be taken to troubleshoot the npm start problem?

I am encountering the error shown below: https://i.stack.imgur.com/jqmcF.png This issue is present on Windows but not on Linux. Which dependency do I need to install? I can't seem to locate the Color npm dependency. ...