Modifying the menu with Angular 4 using the loggedInMethod

Struggling to find a solution to this issue, I've spent hours searching online without success.

The challenge at hand involves updating the menu item in my navigation bar template to display either "login" or "logout" based on the user's current login status.

I utilize two distinct services for this task: the authService, which handles user authentication processes, and the sessionService, responsible for managing session-related functionalities.

The goal is to automatically update components whenever a user logs in or out by syncing with changes made in the localStorage via the sessionService. While attempts with .subscribe and .map have been unsuccessful, calling this.loggedIn.next(this._sessionService.isSetUserSession()); within the login and logout methods seems to be the only approach that works.

What am I missing here?

navbar.component.html

<div class="dropdown-menu" aria-labelledby="dropdownAccount">
 <ng-template *ngIf="(loggedIn$ | async); else elseDiv;">
   <a class="nav-link" href="javascript:void(0)" (click)="logout()">LOGOUT</a>
 </ng-template>
 <ng-template #elseDiv>                       
   <a class="nav-link" href="javascript:void(0)"(click)="login(...);"> 
     LOGIN
   </a>
 </ng-template>
</div>

navbar.component.ts

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

@Component({
    moduleId: module.id,
    selector: 'app-nav-bar',
    templateUrl: 'navbar.component.html',
    styleUrls: ['./navbar.component.css'],
})

export class NavbarComponent implements OnInit {
    isNavbarCollapsed = true;  
    loggedIn$: any;

    constructor(
                private authService: AuthService) {
    }

    ngOnInit() {
        this.loggedIn$ = this.authService.isLoggedIn;
    }
}

auth.service.ts

import {Injectable} from '@angular/core';
import {Http, Response} from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import {IUser} from '../entities/user';
import {SessionService} from './session.service';
import {Router} from '@angular/router';
import {Subject} from 'rxjs/Subject';

@Injectable()
export class AuthService {

    private loggedIn: Subject<boolean> = new Subject<boolean>();

    get isLoggedIn() {
        return this.loggedIn.asObservable();
    }

    constructor(private _http: Http,
                private _sessionService: SessionService,
                private _router: Router) {
        this.loggedIn.next(this._sessionService.isSetUserSession());
    }

    login(user: IUser) {
        return this._http.get('assets/api/responseSuccess.json?email=' + user.email + '&password=' + user.password)
            .map((responseLogin => {
                const jsonResponse = responseLogin.json();
                if (jsonResponse.response === 'success') {
                    const userResponse: IUser = jsonResponse.user;
                    this._sessionService.setUserSession(userResponse);
                    //this.loggedIn.next(this._sessionService.isSetUserSession()); ==> This makes it work, but I want the session to sync automatically without manual calls
                    return true;
                } else {
                   console.log("error logging in");
                    return false;
                }
            }));
    }

    logout() {
        this._sessionService.clearUserSession();
        // this.loggedIn.next(this._sessionService.isSetUserSession()); => Same here

        return this._router.navigate(['/']);
      }
}

session.service.ts

import {Injectable} from '@angular/core';
import {IUser} from '../entities/user';
import {isNullOrUndefined} from 'util';
import {Subject} from "rxjs/Subject";

@Injectable()
export class SessionService {

    isSetUserSession(): boolean {
        return !!localStorage.getItem('user');
    }

    clearUserSession() {
        localStorage.removeItem('user');
    }

    setUserSession(user: IUser) {
        localStorage.setItem('user', JSON.stringify(user));
    }
}

Answer №1

To accomplish this goal, you can avoid using the subject and instead utilize a Shared service.

Define the variable in the service like so:

export class DataService {
   isLoggedIn: boolean = false;
}

In the component, retrieve and update it as shown below:

get data(): string {
    return this.dataService.isLoggedIn;
}
set data(value: string) {
    this.dataService.isLoggedIn = value;
}

Update 1: Alternatively, it can be updated through another service as demonstrated below:

@Injectable()
export class AuthService {
  constructor(public dataService: DataService) { }
  update(value){
    console.log(value)

        this.dataService.isLoggedIn = value;
  }
}

LIVE DEMO

Answer №2

Here are the modifications that need to be made:

Switch from using Subject to BehaviorSubject.

A BehaviorSubject always holds a value, whereas a Subject does not. When subscribing to a BehaviorSubject, it immediately emits the stored value. This is crucial because changes in the nav component won't be reflected when using a Subject.

auth.service.ts

private loggedIn = new BehaviorSubject<boolean>(false);

get isLoggedIn() {
        return this.loggedIn.asObservable();
    }

Make sure to update the loggedIn variable within the login and logout functions.

login(user: IUser) {
        return this._http.get('assets/api/responseSuccess.json?email=' + user.email + '&password=' + user.password)
            .map((responseLogin => {
                const jsonResponse = responseLogin.json();
                if (jsonResponse.response === 'success') {
                    const userResponse: IUser = jsonResponse.user;
                    this._sessionService.setUserSession(userResponse);
               this.loggedIn.next(this._sessionService.isSetUserSession()); 
                    return true;
                } else {
                   console.log("error logging in");
                    return false;
                }
            }));
    }

 logout() {
        this._sessionService.clearUserSession();
        this.loggedIn.next(this._sessionService.isSetUserSession()); 
        return this._router.navigate(['/']);
      }

In navbar.component.ts, make sure to replace loggedIn$: any; with

isLoggedIn$: Observable<boolean>;  

The rationale behind this change is as follows:

By utilizing a BehaviorSubject, the most recent cached value is retained whenever a user logs in or out. Subsequently, when an Observer subscribes to isLoggedIn(), they will instantly receive the stored value based on whether the user is currently signed in or not.

This updated code has been successfully implemented in my current project.

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

I'm currently attempting to set up the React JS package, but unfortunately encountering some errors in the process

I'm having trouble installing react as I keep receiving errors. My system has npm version 8.12.1 installed. I attempted to downgrade react, but that didn't resolve the issue. I tried the following steps: Update npm using: npm install npm -g Dow ...

What is the best way to streamline this using Javascript or jQuery?

Looking for a way to simplify the code for 4 vertical sliding panels? Check out the following script: $(".sliding-panels").click(function() { var n = $(this).attr("number"); var panels = $(".panel"); panels.not(".panel" + n).removeClass("pa ...

Can JQuery's 'unslider' be customized to only change the backgrounds?

Check out my source at this link: http://codepen.io/anon/pen/Bvkjx Observe how the content and background images rotate correctly. Now, I'm curious if it's feasible to keep the following content static <p>SOME CONTENT1</p> while ...

Using JS or jQuery to organize JSON data into a multidimensional array

Dealing with frontend processing of a response from a server that provides event reviews for the season has been challenging. In this case, there can be multiple reviewers for the same event and the simplified version of the response appears as follows: [ ...

What is the best way to display a progress bar as a percentage?

Is there a way to visually represent the progress of an upload on the screen using percentages? I want to display a bar that starts at 0% and updates with the percentage from the percentComplete variable. Once the upload is finished, I'd like to show ...

Is it possible to retrieve messages from a service bus using an Angular app without relying on SignalR?

In our app, we are looking to post messages from our backend to an azure service bus in order to avoid waiting for a long process. Is it possible to do this directly from the front end, or do we need to implement a signalR solution with additional steps on ...

Tips for making sure AngularJS runs a function and its subfunction sequentially

How do I run the code below to display the details of each flavor and its corresponding ITEM ID in a specific order. Execution Format: Flavor1, Flavor2 -- Getflavors() Flavor1 ITEM1, ITEM2... -- GetItemIDs_ofeachFlavor(MapFlvID) GET ITEM1 DETAILS and ad ...

Using Google App Script's pageToken to store attachments on Google Drive

Essentially, my goal is to save all attachments from received emails to a specific folder in Google Drive (mostly .PDF files). However, I've encountered a limitation with the search function which only allows me to retrieve up to 500 attached files. I ...

Retrieve JSON information from the driver's console using Selenium with Python

I am trying to retrieve JSON data from the console, which is shown in this image. Here are the codes I have used so far: j = driver.execute_script(" return document.getElementsByClassName('form-group text required assessment_questions_choices_text ...

Error: karma cannot locate the templateUrl for Angular component

I'm encountering some issues while running tests on angular directives with karma. The problem arises when the directive comes from a templateUrl and is not being translated properly. Here's my karma.conf.js configuration: 'use strict&apos ...

After subscribing, creating the form results in receiving an error message that says "formgroup instance expected."

I am currently working on a project using Angular 6 to create a web page that includes a form with a dropdown menu for selecting projects. The dropdown menu is populated by data fetched from a REST API call. Surprisingly, everything works perfectly when I ...

ngx-timeago encounters errors during deployment of Angular application

When I build my project locally, everything goes smoothly... However, when using aws code build with a docker image and running npx ng build, the following error occurs: #25 41.40 ./node_modules/ngx-timeago/__ivy_ngcc__/fesm2015/ngx-timeago.js:439:76-112 ...

What is the reason for the compatibility issue between sass-loader and Angular?

One of my rules for dealing with sass is as follows: { test: /\.(scss|sass)$/, use: [ { loader: 'raw-loader'}, { loader: 'sass-loader', options: {data: sassConfiguration} } ], } typescript loader { ...

What steps can I take to stop Highcharts from showing decimal intervals between x-axis ticks?

When the chart is displayed, I want to have tick marks only at integer points on the x-axis and not in between. However, no matter what settings I try, I can't seem to get rid of the in-between tick marks. Chart: new Highcharts.chart('<some i ...

Encountered a XHR error (404) while attempting to load the script from https://unpkg.com/[email protected]/operators/index.js/first

Struggling with implementing the Google Material input control in angular2, I keep encountering a recurring error in the browser console. https://i.stack.imgur.com/Q5YFA.png: Upon inspecting my 'node_modules' directory, I noticed the presence o ...

Retrieve the Axios response without interruption, even in the event of an exception, when making API calls with multiple files

Currently, as a beginner diving into Vue.js, I am exploring the world of frontend development by integrating Vue with my Laravel backend. To streamline my API calls and make them more organized for different models, I decided to create a separate file name ...

Cease running code post while loop (do not use break)

I need help with my code that checks if a user is already in a Database. Once the while Loop runs, it verifies the presence of the MC Player in their Database through the Mojang API. However, if the user is already in my Database, I want to skip the Mojang ...

Prevent mat-table rows from collapsing when new data is added (Angular Material)

I'm currently in the process of creating an application that relies on real-time data updates every few seconds. The app utilizes a mat-table, which includes another mat-table within a collapsible row. While the collapsing functionality works properly ...

Can a Node.js dependent library be integrated into Cordova?

After setting up an android project using Cordova, I came across a javascript library that relies on node.js to function. Since Cordova operates with node.js, can the library be invoked within the Cordova environment? ...

The incorrect sequence of Angular/Protractor functions is causing issues within the tests

Trying to extract the values from a column in a grid and compare them before and after sorting can be tricky. I have two functions set up for this task - one to retrieve the initial column values, and another to check the order post-sorting. However, there ...