Tips for managing Asynchronous Local Storage in Angular 2

I am currently working on setting the expiry for a token in local storage by utilizing the angular async local storage module.

import { AsyncLocalStorage } from 'angular-async-local-storage';

const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());

this.localStorage.setItem('expires_at', expiresAt).subscribe(() => {});

Subsequently, I have created a function that is invoked when a user logs in to verify if the token remains valid:

get isAuthenticated(): boolean {
    let res;
    this.localStorage.getItem('expires_at')
        .subscribe((val) => {
            let expiresAt = JSON.parse(val);
            res = (expiresAt && this.loggedIn) ? (new Date().getTime() < expiresAt) : false;
        });

    return res;
}

The issue at hand lies with the fact that localStorage.getItem() returns an Observable. Consequently, when we call .subscribe on the observable, it operates asynchronously, causing the code to continue running without pausing until the result is available. As a result, the return statement executes prematurely, leading to undefined behavior for the variable res, as the subscribe arrow function has not finished executing.

Given this challenge, I seek advice on the most effective solution. One potential approach I considered was attempting to block the execution until the result is ready, but this contradicts the purpose of using the ASYNC local storage module. Perhaps what I require is a straightforward synchronous local storage module instead? However, is this methodology deemed ill-advised or bad practice? I would greatly appreciate any guidance or insights on this matter. Thank you!

Answer №1

To cut a long story short: I recommend going with a synchronous local storage solution in your case.

Going into more detail:

When faced with the choice between synchronous and asynchronous solutions in Angular, I typically find myself opting for one of these two approaches:

1) Have isAuthenticated return an Observable and map your local storage observable to provide a true/false value

get isAuthenticated(): Observable<boolean> {
    return this.localStorage.getItem('expires_at').pipe(
        map((val) => {
            let expiresAt = JSON.parse(val);
            let res = (expiresAt && this.loggedIn) ? (new Date().getTime() < expiresAt) : false;
            return res;
        })
    );
}

2) Choose a synchronous solution as you suggested

The best practice depends on the situation at hand. In my experience, handling things the observable way tends to result in more code, dealing with all possible scenarios. Additionally, any dependencies on this code must be called asynchronously too (for example, if you are logged in, then send an HTTP request followed by rendering its response). To manage such things efficiently and systematically using services, you would need to delve deeper into RxJS and its operators such as MergeAll etc...

On the other hand, employing a synchronous solution would simplify your code significantly, but there is a risk that your application may slow down in certain situations (such as synchronous HTTP requests on a sluggish mobile network).

If you are fetching only a small item from localStorage just once, the difference may not be noticeable. However, this library offers a uniform interface for IndexedDb where the maximum limit can be up to 2Gb of data. Storing entire collections there can greatly enhance your app's speed if you opt for the asynchronous approach.

Answer №2

Assuming that the user is already prepared to wait for an asynchronous login process to load, you can link your localStorage call to it using flatMap:

checkAuthentication(): Observable<number> {
    return this.localStorage.getData('expiration_time')
    .map((data) => {
            let expirationTime = JSON.parse(data);
            return (expirationTime && this.loggedIn) ? (new Date().getTime() < expirationTime) : 0;
        });
}

processUserLogin()
.flatMap(loginResponse => {
    return this.checkAuthentication()
})
.subscribe(result => {
    // handle the outcome as needed
}

Keep in mind that checkAuthentication() cannot have a return type of boolean or Observable<boolean> while returning a timestamp. Therefore, I replaced false with 0.

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

"An error has occurred stating that the header is not defined in

It is a coding issue related to payment methods. The headers type is undefined in this scenario, and as a newcomer to typescript, pinpointing the exact error has been challenging. An error message is indicating an issue with the headers in the if conditio ...

Can Angular reactive forms be used to validate based on external conditions?

Currently, I am exploring Angular reactive forms validation and facing an issue with implementing Google autocomplete in an input field: <input autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="input-auto input" formControlName ...

Can we break down and explain iterative dynamic imports with conditions in JavaScript?

Is there a way to simplify the named imports and assignments in my code programmatically without repeating myself? I am looking for a solution that involves using a loop. This is what I currently have: import { globalLocale } from './i18n' let ...

How can you create an interface where the value type is determined by the key, but not all keys are predefined?

Below is an illustration of a data structure that I aim to define as a type in TypeScript: const dataExample = { Folder: { "Filename.js": { lines: { total: 100, covered: 80, ...

Is there a more efficient method for invoking `emit` in Vue's Composition API from an external file?

Is there a more efficient way to access the emit function in a separate logic file? This is my current approach that is functioning well: foo.js export default (emit) => { const foo = () => { emit('bar') }; return { foo }; } When ...

Changing environment variables for jasmine tests

My Angular service code snippet includes importing the environment like this: import {environment} from '../environment' .... public something() { if(environment.production) { // do stuf } else { // do something else } } I am now l ...

What are some ways to enhance the design of Material Input Text boxes and make them more compact?

I have been developing an Angular application using Material UI v7, but I am finding that the input controls are not as compact as I would like them to be. I have attempted to customize them without success. Can someone provide guidance on how to achieve m ...

What is the best way to retrieve an item from an array?

I have an array containing multiple records and I need to extract just one record from it using its id as an object. However, the result I'm getting is an array with that single record. Is there a way to resolve this issue? Result: [{…}] 0: {id ...

Is it possible to safely remove a class instance containing a GLcontext within a react.FC State to prevent memory leaks, especially when using a "class object with THREE.js"?

I have successfully developed a react.FC() application. In this application, you have the ability to throw a bottle in the metaverse (like a message in a bottle) to be discovered in the future. The app retrieves information from an API and constructs a c ...

"Triggering a click event on the unordered list within an Angular 2

Looking to retrieve the class name when clicking on any item in a list. <ul id='color' name='color' class="choose-color" (click)=getColor()> <li class="color1"></li> <li class="color2"&g ...

Ways to incorporate padding into md-card using Angular 2 material

Need assistance with adding padding to md-card in angular2 material. I am using md-card to display my product list but struggling to add padding on them. Here's what I have tried: product.component.html : <p> Product List </p> <div ...

Zod data structure featuring optional fields

Is there a more efficient way to define a Zod schema with nullable properties without repeating the nullable() method for each property? Currently, I have defined it as shown below: const MyObjectSchema = z .object({ p1: z.string().nullable(), p2 ...

Verifying Angular (2+?) Compatibility: Opening and Closing Material mat-menu on Hover [GUIDE]

After extensive research, I tried various methods to hover a material menu to display its options. However, the solutions I came across were either too complicated or ineffective. Therefore, I decided to develop my own solution by combining elements of e ...

Ways to turn off the dragging animation for cdkDrag?

I am facing an issue with cdkDrag where a small preview of the item being dragged shows up. I want to disable this feature and remove any CSS classes related to the dragging state. Can anyone guide me on how to achieve this? https://i.sstatic.net/nYU07.pn ...

Tips for displaying and hiding content in Angular2

I have a component that toggles the visibility of elements by clicking a button. This is the snippet of my HTML code: <div *ngFor="let history of histories | sortdate: '-dateModified'"> <p><b>{{ history.remarks }}</b& ...

Sending image from angular2 to asp.net core server

Currently, I have an asp.net core application with angular2 and I am facing an issue with uploading images. While I was able to upload images as byte[], I encountered a problem in checking if the uploaded file is actually an image on the backend. This led ...

Display a notification next to the pinned location on the map using the Google Maps API

I'm trying to get this popup to show up close to the marker. Can anyone help me figure out how to do it? Here is my current setup: .html <agm-map [latitude]="lat" [longitude]="long" style="height: 350px;" [fitBounds]="true"> <agm-marker ...

Having issues with my custom NextJS server implementation using TypeScript

I am currently exploring the use of a custom server with NextJS in conjunction with TypeScript. In order to do this, I must utilize the nextjs createServer function. Everything is functioning correctly when using the require syntax: const next=require(&ap ...

The specified instant cannot be located in 'moment' while attempting to import {Moment} from 'moment' module

Struggling in a reactJS project with typescript to bring in moment alongside the type Moment Attempted using import moment, { Moment } from 'moment' This approach triggers ESLint warnings: ESLint: Moment not found in 'moment'(import/n ...

Tips for triggering a function each time a view is shown

I am currently developing an inappbrowser application that involves communication between the webview and the app. Below is a snippet of my code : import { Component } from '@angular/core'; import { NavController, Platform } from 'ionic-an ...