Tips to successfully save and retrieve a state from storage

I've encountered a challenge while working on my Angular 14 and Ionic 6 app. I want to implement a "Welcome" screen that only appears the first time a user opens the app, and never again after that.

I'm struggling to figure out how to save the state of whether the user has already seen the screen or not, so the app won't display it on subsequent openings...

My idea was to create a storage service to store this state, and then use a route guard for redirection and displaying the page...

However, I'm now facing an error and I'm unsure how to resolve it. Am I correctly storing the value in the storage service, or is there something fundamentally wrong with my code? ERROR:

Error: src/app/guards/first-load.guard.ts:17:50 - error TS2339: Property 'then' does not exist on type 'void'. [ng] [ng] 17
this.storageService.getValue('first_time').then((value) => { [ng]

Here's my code snippet:

storage.service.ts:

export class StorageService {
  constructor(
    private storage: Storage
  ) {
    this.init();
  }

  async init() {
    await this.storage.create();
  }
  setValue(key: 'first_time', value: 'done') {
    this.storage.set(key, value);
  }

  getValue(key: 'first_time') {
    this.storage.get(key).then((value) => {
      console.log(value);
    });
  }
}

In the first-load.guard.ts file, I am using this service as follows:

export class FirstLoadGuard implements CanActivate {
  constructor(
    private storageService: StorageService,
    private router: Router
  ) {}
  canActivate(route: ActivatedRouteSnapshot):  Promise<boolean>
  {
    return new Promise(resolve => {
      this.storageService.getValue('first_time').then((value) => {
        if (value !== null) {
          this.router.navigateByUrl('/login');
          resolve(false);
        }
        else {
          this.storageService.setValue('first_time', 'done');
          resolve(true);
        }
      });
    });
  }
}

If more code snippets are needed, please feel free to leave a comment :) Hoping for some assistance!

Answer №1

Your function definitions are causing the issue. Using : declares a format, but you seem to be defining variables as constants which may result in null values.

To fix this, update key: 'first_time' and value: 'done' in your storage service to key: string and value: string. Alternatively, if you want default values for your function inputs:

getValue(key: string = 'first_time')

If your inputs are truly constant and not meant to be inputs at all, it's better to define them within your service instead of taking them as inputs:

key= 'first_time';
value= 'done';
setValue() {
    this.storage.set(this.key, this.value);
  }

getValue() {
    this.storage.get(this.key).then((value) => {
      console.log(value);
    });
  }

Answer №2

You are receiving the error message

Error: src/app/guards/first-load.guard.ts:17:50 - error TS2339: Property 'then' does not exist on type 'void'. [ng] [ng] 17 this.storageService.getValue('first_time').then((value) => { [ng]

This issue arises because the storageService.getValue() function does not actually return anything. To resolve this error, you need to ensure that the promise with the value from the storage is returned:

  getValue(key: 'first_time') {
    return this.storage.get(key).then((value) => {
      console.log(value);
      return value;
    });
  }

If you eliminate the console.log() statement, the function can be simplified as follows:

  getValue(key: 'first_time') {
    return this.storage.get(key);
  }

Furthermore, here are some additional comments on your code:

In response to @AmirAli Saghaei's answer, it seems that you are defining the arguments of the function as static values, which might lead to type errors if different argument values are passed to setValue(). If you intend to provide default values for the arguments, use = instead of ::

  setValue(key = 'first_time', value = 'done') {
    // ...
  }

For specifying the expected type, use string:

  setValue(key: string, value: string) {
    // ...
  }

The usage of async/await in init() does not make a difference since it is called synchronously by the constructor. Using async/await in the constructor would require the constructor to return a promise, which is considered bad practice.

In the route guard, there is no need to create a new Promise(). You can simply chain and return the promise from getValue:

  canActivate(route: ActivatedRouteSnapshot): Promise<boolean>
  {
    return this.storageService.getValue('first_time').then((value) => {
      if (value !== null) {
        this.router.navigateByUrl('/login');
        return false;
      }
      else {
        this.storageService.setValue('first_time', 'done');
        return true;
      }
    });
  }

Alternatively, you can utilize async / await:

  async canActivate(route: ActivatedRouteSnapshot): Promise<boolean>
  {
    const value = await this.storageService.getValue('first_time');
    if (value !== null) {
      this.router.navigateByUrl('/login');
      return false;
    }
    else {
      this.storageService.setValue('first_time', 'done');
      return true;
    }
  }

A more concise version of your code could be:

export class StorageService {
  constructor(
    private storage: Storage
  ) {
    this.storage.create();
  }

  setValue(key: string, value: string) {
    this.storage.set(key, value);
  }

  getValue(key: string) {
    return this.storage.get(key);
  }
}

export class FirstLoadGuard implements CanActivate {
  constructor(
    private storageService: StorageService,
    private router: Router
  ) {}

  async canActivate(route: ActivatedRouteSnapshot): Promise<boolean>
  {
    const value = await this.storageService.getValue('first_time');
    if (value !== null) {
      this.router.navigateByUrl('/login');
      return false;
    }
    else {
      this.storageService.setValue('first_time', 'done');
      return true;
    }
  }
}

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

Loading Objects with Material Textures Ahead in Three.js

I am faced with the challenge of preloading multiple obj+mtl files using Three.js, each with distinct file paths, and I need to trigger another function once all the objects have finished loading. Initially, I attempted to use a boolean variable that woul ...

Setting a dynamic routerLink in Angular 2 based on a component's property-value

Recently, I developed a component with a <a> element and a routerLink property that I intended to set from the template in which the component is used. However, when attempting to do so, I encountered an error message stating 'Cannot read proper ...

Issue: In Firefox, resizing elements using position:absolute does not work as expected

My resizable div code looks like this: #box { height: 10rem; width: 10rem; resize: both; overflow: hidden; border: solid black 0.5rem; position: absolute; pointer-events: none; } <div id="item"></div> While it works perfectly ...

When working with Angular, the onSubmit method may sometimes encounter an error stating "get(...).value.split is not a function" specifically when dealing with Form

When the onSubmit method is called in edit, there is an error that says "get(...).value.split is not a function" in Form. // Code for Form's onSubmit() method onSubmitRecipe(f: FormGroup) { // Convert string of ingredients to string[] by ', ...

Error message in Ionic 2: "Property is not found on type"

Currently, I am working on a project in Ionic 2 and have encountered a stumbling block with a seemingly simple task. My issue lies with a Textbox where I aim to input text that will then be displayed. I found some code on a website (http://www.tizag.com/j ...

Guide to integrating jssor into an AngularJS application

Struggling to integrate jssor with angularjs, it seems to be failing because jssor is being initialized before angularjs, causing the ng-repeat elements to not resolve correctly. <div id="slider1_container"> <div u="slides"> <!-- Th ...

Achieving consistent scroll position when utilizing the history.js library to navigate back with the click of a button

I need help implementing a feature that allows users to return to the same position on a webpage when clicking the back button. A common example is on ecommerce sites where if you scroll down, click on a product, then go back, you should be taken back to t ...

Is it possible to use async in the onChange handler of a React event?

Can async be used to pause execution until a function completes within an onChange event? Here is an example: const onChange = async (e) => { console.log(current[e.target.name]); await setCurrent({ ...current, [e.target.name]: e.targe ...

Tips for embedding Javascript code within the window.write() function

I have a JavaScript function that opens a new window to display an image specified in the data variable. I want the new window to close when the user clicks anywhere on it. I've tried inserting some JavaScript code within window.write() but it doesn&a ...

I am having trouble getting event handlers to work with a group of buttons in JavaScript

I'm facing a problem where I'm attempting to add event handlers to buttons stored in an array. Upon clicking a button, it should trigger a function, but for some reason, it's not working and I can't seem to identify the issue. Below is ...

Expo is having trouble locating the module "color-convert" - such a frustrating problem

Having an issue! I ran the command npm start in my terminal and received the following error: Starting project at /home/pc/Documents/Projects/Mobile/weather_app Developer tools running on http://localhost:19002 Cannot find module 'color-convert' ...

How to access enums dynamically using key in TypeScript

export enum MyEnum{ Option1, Option2, Option3 } string selection = 'Option1'; MyEnum[selection] results in an error: The type string cannot be assigned to the type MyEnum On the other hand: MyEnum['Option1'] works as ...

Error message "Uncaught in promise" is being triggered by the calendar function within the Ionic

Can someone assist me in creating a calendar feature for my app? My concept involves a button with text that, when clicked by the user, opens a calendar. However, I am encountering an error message: ERROR Error: Uncaught (in promise): TypeError: Cannot set ...

How can I combine multiple requests in RxJS, executing one request at a time in parallel, and receiving a single combined result?

For instance, assume I have 2 API services that return data in the form of Observables. function add(row) { let r = Math.ceil(Math.random() * 2000); let k = row + 1; return timer(r).pipe(mapTo(k)); } function multiple(row) { let r = Math.c ...

Leveraging Angular to dynamically adjust the height of ng-if child elements based on parent

I'm struggling with a few things in my current setup. I have a view that consists of 3 states - intro, loading, and completed. My goal is to create a sliding animation from left to right as the user moves through these states. Here is the basic struc ...

Testing the equality of nested arrays: A step-by-step guide

My maze generator creates walls for each "cell", resulting in duplicate walls - such as the left wall of one cell being identical to the right wall of the adjacent cell. I have managed to convert the maze data into a different program where it is stored in ...

Tips for eliminating the left and bottom border in a React chart using Chart.js 2

Is there a way to eliminate the left and bottom borders as shown in the image? ...

Tips for creating a dynamic curved SVG path

I'm looking to draw a black border only along the inside of this SVG. I've tried adding stroke and setting the stroke-width, but that applies the border to the entire SVG. Is there a way to limit the stroke to a certain point within the SVG? d ...

What is the best way to insert a chart into a div using *ngIf in Angular?

I just started using chart.js and successfully created the desired chart. However, when attempting to implement two tab buttons - one for displaying tables and the other for showing the chart - using *ngIf command, I encountered an error: Chart.js:9369 F ...

What is the best way to activate ngModelChange for a custom input field?

I have developed a custom input component and here is the code: html: <input class='ctrl__counter-input' maxlength='10' type='text' [value]="counterValue" [ngModel]="counterValue" (ngModelChange)="onKey(in ...