Set the default requests header in Ionic 3 using data stored in Ionic Storage

This particular issue is related to retrieving a value from local storage. I am trying to set the default header (Authorization token) for all requests, but I can't seem to find a solution that works efficiently. Most of the resources available only explain how to set it for each individual request, which seems redundant. Why set it repeatedly when it could be set universally?

In my attempts:

Case #1:

I have tried creating a custom RequestOptions class to handle this logic, but unfortunately, it is not compiling as expected.

Case #2:

Another approach involved using Observable and flatMap to retrieve the API token from storage and set the Authorization header. However, this resulted in an error being thrown.

Case #3:

The attempt to use 'await' keyword with storage.get() did not work as intended.

Any suggestions or insights on how to tackle this problem more effectively?

Answer №1

Incorporating this feature into my Ionic Application was quite seamless, as demonstrated below:

To begin, I crafted a personalized Http Interceptor,

http.interceptor.ts

import { Events } from 'ionic-angular';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import 'rxjs';
import { Storage } from '@ionic/storage';
import {Http, RequestOptionsArgs, Response, RequestOptions, ConnectionBackend, Headers} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {Storage} from '@ionic/storage';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/map';

export class HttpInterceptor extends Http {
  constructor(connectionBackend: ConnectionBackend, requestOptions: RequestOptions, public storage: Storage) {
    super(connectionBackend, requestOptions);
  }

  public get(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.get(url, options)
    });
  }

  public post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.post(url, body, options)
    })
  }

  public put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.put(url, body, options)
    })
  }

  public delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.delete(url, options)
    });
  }

  private getRequestOptionArgs(options?: RequestOptionsArgs) {
    return this.storage.get('token').then((token) => {
      if (options == null) {
        options = new RequestOptions();
      }

      if (options.headers == null) {
        options.headers = new Headers();
      }

      if (token !== null) {
        options.headers.append('Authorization', 'Bearer ' + token);
      }
      options.headers.append('Content-Type', 'application/json');

      return options;
    });
  }
}

Within app.module.ts

app.module.ts

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {IonicApp, IonicModule} from 'ionic-angular';
import {Storage, IonicStorageModule} from '@ionic/storage';
import {HttpModule, XHRBackend, RequestOptions, Http} from '@angular/http';
import {HttpInterceptor} from '../auth/http.interceptor';
import {SplashScreen} from "@ionic-native/splash-screen";
import {StatusBar} from '@ionic-native/status-bar';
import {Keyboard} from '@ionic-native/keyboard';
import {InAppBrowser} from '@ionic-native/in-app-browser';

export function httpInterceptorFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions, storage: Storage) {
  return new HttpInterceptor(xhrBackend, requestOptions, storage);
}

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp, {mode: 'md'}),
    IonicStorageModule.forRoot(),
    HttpModule
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    ...
  ],
  providers: [StatusBar, SplashScreen, Keyboard, InAppBrowser, 

    provide: Http,
    useFactory: httpInterceptorFactory,
    deps: [XHRBackend, RequestOptions, Storage]
    

  ]
})

export class AppModule {
}

Subsequently, in the provider, I utilized it akin to a standard http request

app.provider.ts

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { CONFIG } from '../config/app.config';

@Injectable()
export class AppProvider {
  private baseUrl:string = CONFIG.apiEndpoint;

  constructor(private http: Http) {

  }

  public getSomething():Observable<any> {
    let url:string = this.baseUrl + 'some endpoint';

    return this.http.get(url).map((res:Response) => res.json());
  }
}

This implementation proved beneficial for my application's functionality.

Answer №2

To achieve this goal, one effective method is through the implementation of Interceptors. However, as of the time of writing this response, Ionic is currently using Angular 4.1.3, which utilizes HttpModule without native interceptor support. The upcoming version of Angular 4.3.1 (https://angular.io/guide/http) introduces HttpClientModule with built-in Interceptors functionality. The approach with interceptors involves intercepting the request, accessing data from storage (generating a promise), and upon resolution, triggering next.handle(req) to proceed to the API request stage.

For now, I am implementing a temporary solution (which may become unnecessary once Ionic updates to Angular 4.3.1 soon... hint hint) by creating a "custom" interceptor through extending Http (https://medium.com/aviabird/http-interceptor-angular2-way-e57dc2842462).

Wishing you success in your endeavors!

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

Global installation of Node modules

How do I reference globally installed node modules? For example, if I have a package.json file and I choose to install all the node modules listed in it globally (located at C:\Users\MyaccountName\AppData\Roaming\npm), how can I ac ...

Using debounceTime and distinctUntilChanged in Angular 6 for efficient data handling

I recently came across a tutorial on RxJS that demonstrated the use of debounce and distinctUntilChanged. I'm trying to implement it in Angular 6, but I'm facing some challenges. Here is the code from the tutorial: var observable = Rx.Observabl ...

Unable to access 'this' within a custom operator in RxJs

I developed a unique operator that utilizes the this keyword, but I am encountering an issue where it always returns undefined. Even though I used bind to pass this into the function. My special operator function shouldLoadNewOptimizationData() { retu ...

The correct way to add to a string array that has been passed as props to a functional component

There is a parent component that establishes a useState hook with a string array. Then, there is a child component tasked with updating the string array. To accomplish this, both the string array and the useState function are passed to the child component. ...

Employing async await for postponing the execution of a code block

I have been working on an Angular component where I need to perform some actions after a data service method returns some data asynchronously. Although I attempted to use async/await in my code, I feel that I may not have fully understood how it works. Her ...

TypeScript encountered an error with code TS2554, indicating that it was expecting 0 arguments but instead received 1 in an Ionic application

Hello everyone! I'm encountering an issue in my project involving a Type Script error TS2554: Expected 0 arguments, but got 1. This error is preventing me from being able to select other options for custom input pop up. In this forum post, I have shar ...

Solving issues with malfunctioning Angular Materials

I'm facing an issue with using angular materials in my angular application. No matter what I try, they just don't seem to work. After researching the problem online, I came across many similar cases where the solution was to "import the ...

Is it possible to send a JSON object to a RESTful API in Angular 2+ without relying on cookies?

Struggling to send a json type object to the rest service in an (angular2+ springMvc + java) web project. It's proving to be quite challenging, and on top of that, I'm unable to utilize cookies too. ...

Despite the error message stating that it cannot find module 'angular2/core', the application is still functioning properly

Imagine you have an Angular2 application with a file named app.component.ts that contains some import statements: import {Component} from 'angular2/core'; import {FiltersService} from "./services/filters-service"; import {SearchPipe} from "./ ...

"ng2-file-uploader necessitates a browser refresh in order to function

I am currently utilizing ng2-file-upload within my Angular 10 project to allow users to upload their photos. The upload process is functioning correctly, but the issue arises when the newly uploaded photo does not automatically appear in the photo list wit ...

Guide to testing template-driven forms in Angular 6

Currently, I am working on a template-driven form which looks like this: <form #form="ngForm" (ngSubmit)="onSubmit()"> <input class="form-control input-lg" id="venue_name" name="venue_name" type="text" #venue_name="ngModel" [(n ...

"Creating a Typescript property that is calculated based on other existing properties

I am working on a Typescript project where I have a basic class that includes an `id` and `name` property. I would like to add a third property called `displayText` which combines the values of these two properties. In C#, I know how to achieve this using ...

The blur event in Angular Material's mat-date-range-input does not trigger if the End Date is not selected

I am currently utilizing Angular 15 along with Angular Material 14. The code for the DatePicker control is shown below. <mat-form-field class="datepicker-widget" appearance="fill"> <mat-date-range-input [formGroup]="d ...

Creating a Versatile Data Retrieval Hook in TypeScript for APIs with Diverse Response Formats

Currently, I am undertaking a movie discovery project that involves fetching data from two APIs: Tmdb movie site. These APIs have different response structures—one for movies and one for genres. In order to streamline my code and avoid repeating the same ...

Angular 8 mat-sort issue: malfunctioning sort functionality

I have successfully implemented sorting in a mat-table within an angular 8 project. While referring to the guide available at https://material.angular.io/components/sort/overview, I encountered an issue where the sorting functionality is only working for s ...

Efficiently handling heavy components in Angular using ngFor

Encountered an issue: I have an array containing chart configurations that need to be displayed. Currently, I am iterating through the array and rendering the charts as shown below: <ng-container *ngFor="let config of configs; trackBy: getId"& ...

I am interested in using a loop in Angular to highlight my div element

Enhancing my comprehension regarding the mentioned images. If I don't select anything within the div property, the default style (css) should appear like this, at least when one div is selected. However, the issue arises when unable to select. This ...

What is the best way to update the value of a Material Angular select to match its label in TypeScript?

Is there a way to reset the value of this select element back to <mat-label>Select Member</mat-label> in TypeScript when a specific event occurs? I am currently unable to find a solution on the TypeScript side. Any advice would be appreciated ...

Steps to automatically make jest mocked functions throw an error:

When using jest-mock-extended to create a mock like this: export interface SomeClient { someFunction(): number; someOtherFunction(): number; } const mockClient = mock<SomeClient>(); mockClient.someFunction.mockImplementation(() => 1); The d ...

I'm facing some issues with my initial attempt at an Angular application and it's

After diving into Angular and setting everything up, I encountered an issue where instead of seeing a name displayed, I was getting {{ name }}: Below is the content of app.component.html: <input type="text" [(ngModel)]="name"> <p>{{ name }}&l ...