Angular 2/4 - Saving User Object Information in the Front-End Instead of Repeatedly Contacting the Back-End Server

Is there a more efficient way to store and update the current user details in the frontend, without constantly making new HTTP GET requests to the backend every time a new component loads?

The solution I came up with is a UserService class that handles setting and updating a currentUser object whenever a method inside the class is called.

import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
export class UserService {

    public currentUser: any;

  constructor(private http: Http) { }

  updateCurrentUser() {
    this.http.get('api/login/currentUser').subscribe(data => {
        this.currentUser = data;
    });
  }

}

However, this approach has resulted in race condition problems. For instance, if the profile component tries to access currentUser.username before the service class completes the request, an error occurs.

I attempted to resolve this issue by checking if currentUser is undefined and then calling updateCurrentUser(), but it did not solve the problem.

if(this.userService.currentUser === undefined){
    this.userService.updateCurrentUser();
} 


Update:An error message is displayed in the browser console https://i.sstatic.net/K0ioy.png


Further Details:
I am using data binding to show the username directly from the UserService class. For example:

<span class="username-block">{{userService.currentUser.username}}</span>

I also attempted assigning the currentUser object to a variable within the component attempting to display the username, but encountered the same outcome.


Any suggestions or feedback would be greatly appreciated.

Answer №1

If you want to implement the Elvis/existential operator in your HTML, you can use it by adding a question mark after currentUser to prevent evaluating username if currentUser is undefined.

Once your asynchronous code fills in the data, the page will display it accordingly.

<span class="username-block">{{userService.currentUser?.username}}</span>

Edit

I see that you are outputting userService.currentUser.username. To ensure reactive data handling, consider returning an observable from the service to the component like this:

updateCurrentUser() {
    return this.http.get('api/login/currentUser')
      .catch(error => this.handleError(error, 'currentUser'));
}

Then in the component:

private currentUser;

ngOnInit() {
  this.userService.updateCurrentUser()
    .take(1)
    .subscribe(data => { this.currentUser = data });
}

The html now references the local component copy which will be filled in by the subscription over time. Note that take(1) is used to close the subscription and prevent memory leaks.

    <span class="username-block">{{currentUser?.username}}</span>

Edit #2

To avoid multiple HTTP calls, adjust the service code as follows:

export class UserService {

  private currentUser: any; // private because we only want to access through getCurrentUser()

  constructor(private http: Http) { }

  getCurrentUser() {
    return this.currentUser
      ? Observable.of(this.currentUser) 
      : this.http.get('api/login/currentUser')
          .do(data => { this.currentUser = data }) // cache it for next call
          .catch(error => this.handleError(error, 'currentUser'));
  }

Answer №2

If you are utilizing routing in your application, consider implementing a route resolver on the initial route that requires user data. This resolver will ensure that the view is only displayed once the necessary data has been fetched.

As an illustration, here is an example of a route resolver I have utilized. Take note that it calls a service to fetch the data.

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { Observable } from 'rxjs/Observable';

import { IMovie } from './movie';
import { MovieService } from './movie.service';

@Injectable()
export class MovieResolver implements Resolve<IMovie> {

    constructor(private movieService: MovieService) { }

    resolve(route: ActivatedRouteSnapshot,
            state: RouterStateSnapshot): Observable<IMovie> {
        const id = route.paramMap.get('id');
        return this.movieService.getMovie(+id);
    }
}

The resolver should then be included in the relevant route configuration, for instance:

  {
    path: ':id',
    resolve: { movie: MovieResolver },
    component: MovieDetailComponent
  },

In this setup, the route will not be visible until the required data has been obtained.

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

React Traffic Light Component: Colors Stuck After Timeout

I've been working on solving a React issue and followed a tutorial on YouTube. I'm using CodeSandbox for my project, but I'm facing a problem where the colors of the signal are not showing up and do not change after some time. I even tried u ...

How to resolve the issue of "NGUniversal window is not defined"

I have a newer version of Angular in my application and recently integrated ng-universal. When I attempt to run the command npm run dev:ssr, an error is thrown with the message: ERROR ReferenceError: window is not defined This error originates from a li ...

Error: Unable to convert null or undefined to an object | NextAuth

Recently, I've been attempting to implement a SignIn feature with Nextauth using the following code: import { getProviders, signIn as SignIntoProvider} from "next-auth/react"; function signIn({ providers }) { return ( <> ...

Is there a way to trigger a function upon the loading of a template in Angular 2?

I'm a newcomer to angular2 and I need to trigger a function when a template loads or initializes. I have experience with achieving this in angular1.x, but I'm struggling to figure out how to do it in angular-2. Here's how I approached it in ...

Attempting to start an Angular project using NG NEW constantly fails nowadays - always ends with error code EPERM

Can Angular still be considered a reliable framework when pervasive errors and bugs persist for extended periods without any clear resolution documented? .... 24695 silly saveTree | +-- <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cf ...

FullCalendar mysteriously missing from SystemJS Builder bundle

I am currently using the SystemJS builder to compile an Angular 2 application with PrimeNg components. The issue arises when trying to integrate the PrimeNg schedule component which relies on FullCalendar. Although the builder runs without errors, when I a ...

Receiving an error when attempting to inject the Router in a component constructor without using the elvis operator

Upon launching my app, I desire the route /home to be automatically displayed. Unfortunately, the Angular 2 version I am utilizing does not support the "useAsDefault: true" property in route definitions. To address this issue, I considered implementing th ...

Inadequate execution of a Faux Router

UPDATE: After following the advice provided in the solution below, I realize that I confused router testing methods. I have made the necessary changes to my code and the tests are now passing, but I am encountering this warning: WARN LOG: 'Navigation ...

Use `$$state: {…}` within the request rather than the data

When attempting to send a request with data, I am only getting "f {$$state: {…}}" in the console. $scope.createTask = function () { var req = $http.post('api/insert', { title: $scope.newTitle, description: ...

Exploring Local Gems with Google Places API in Ionic 2

I recently integrated the Google Maps API into my app and now I am looking to incorporate Google Places as well. I have managed to successfully implement Geolocation, but I am facing difficulties when trying to perform a nearby search for "supermarkets" in ...

Retrieve an established SQS eventSource in AWS CDK

When working with AWS CDK, there is a built-in function called addEventSource that allows you to easily add new SQS triggers (eventSources) to a lambda function. However, I'm curious if there is a way to access and modify the existing eventSources ass ...

TypeScript interface with an optional parameter that is treated as a required parameter

Within my interface, I have a property that can be optional. In the constructor, I set default values for this property, which are then overridden by values passed in as the first parameter. If no properties are set, the defaults are used. I am looking fo ...

Is it possible that Typescript does not use type-guard to check for undefined when verifying the truthiness of a variable?

class Base {} function log(arg: number) { console.log(arg); } function fn<T extends typeof Base>( instance: Partial<InstanceType<T>>, key: keyof InstanceType<T>, ) { const val = instance[key]; if (val) { ...

Preventing Redundancy in Angular 2: Tips for Avoiding Duplicate Methods

Is there a way I can streamline my if/else statement to avoid code repetition in my header component? Take a look at the example below: export class HeaderMainComponent { logoAlt = 'We Craft beautiful websites'; // Logo alt and title texts @Vie ...

Is it possible to create a TypeScript class that contains various custom functions?

Exploring TypeScript is a fresh yet exciting journey for me! In the world of JavaScript, checking if an object has a function and then calling it can be achieved with code like this: if(this['my_func']) { this['my_func'](); } Howeve ...

*ngFor not functioning properly within Angular ionic modal

While working on my Ionic application with Angular, I encountered an issue with the ngForm not functioning properly inside a modal. I have tried to identify the problem with a simple code snippet: <li *ngFor="let item of [1,2,3,4,5]; let i = index ...

Hold off on utilizing information from a single observable until a later time

In my Angular component, I am working with the following code: @Component({...}) export class ComponentOne implements OnDestroy, OnChanges { readonly myBehaviourSub = new BehaviorSubject<Observable<MY_CUSTOM_INTERFACE>>(NEVER); constructo ...

Having trouble getting a basic file upload to work in Angular2+? Wondering what the easiest method is to make it function properly? Look no

I successfully created a MEAN stack CRUD board using Angular 2+, Node.js, Express, and MongoDB. However, I encountered an issue when trying to add an upload function. The error message displayed was: compiler.js:486 Uncaught Error: Template parse errors: ...

Display a semantic-ui-react popup in React utilizing Typescript, without the need for a button or anchor tag to trigger it

Is there a way to trigger a popup that displays "No Data Found" if the backend API returns no data? I've been trying to implement this feature without success. Any assistance would be greatly appreciated. I'm currently making a fetch call to retr ...

Angular 5 combined with Electron to create a dynamic user interface with a generated Table

I am working on an Angular Pipe: import {Pipe, PipeTransform} from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import * as Remarkable from 'remarkable'; import * as toc from 'markdown-toc&a ...