What is the best way to extract values from case-sensitive query param variables?

I am dealing with a URL that contains the query string id. However, the variable id can appear as either 'id' or 'Id' in the URL.

From my understanding, these two variations will be treated differently. To handle URLs like the following:

http://xxx/abc?id=10

http://xxx/abc?Id=10

private findQueryParams() {
  this._router.queryParams.subscribe((params: Params) => {
    if (Object.keys(params).length) {
      let id = 0;
      if (params['id']) {
        id = Number(params['id']);
      } else if (params['Id']) {
        id = Number(params['Id']);
      }
    }
  });
}

Is there a simpler way to handle case-sensitive variables and retrieve their values accurately in angular4?

Answer №1

Referring to @vogomatix's advice, it is recommended not to have the same parameter with different names. However, if for some reason you do need to, here's a simplified version of your code:

private findQueryParams() {
    this._route.queryParams.subscribe((params: Params) => {
        const idValue = params['id'] || params['Id'] || '0';
        let id = Number(idValue);
    });
}

This will prioritize the value of id, then fallback to Id, and finally default to '0' if none exist.

If there are multiple variations of parameter names as mentioned, a better approach would be to convert all keys to lowercase:

function toLower(params: Params): Params {
    const lowerParams: Params = {};
    for (const key in params) {
        lowerParams[key.toLowerCase()] = params[key];
    }

    return lowerParams;
}

With this modification:

private findQueryParams() {
    this._route.queryParams.subscribe((params: Params) => {
        const _params = toLower(params);
        const idValue = params['id'] || '0';
        let id = Number(params);
    });
}

It's necessary to apply this conversion every time you retrieve parameters from the Router, as the issue lies within the URL system itself.

Answer №2

After searching online without success, I decided to create my own versatile class that wraps the existing Params object and allows for case-insensitive access to query string parameters.

Here's how you can incorporate it into your component:

import { ActivatedRoute } from '@angular/router';
import { CaseInsensitiveParamMap } from '../case-insensitive-param-map';

constructor(private activatedRoute: ActivatedRoute) {}

ngOnInit(): void {
  // Subscribe to changes in query params:
  this.activatedRoute.queryParams.snapshot.paramMap
    .subscribe((params) => this.handleQueryParamsChanged(new CaseInsensitiveParamMap(params)));

  // Or directly use params from the route snapshot:
  const paramMap = new CaseInsensitiveParamMap(this.activatedRoute.snapshot.queryParams);
  const returnUrl = paramMap.get('returnUrl');
}

private handleQueryParamsChanged(paramMap: CaseInsensitiveParamMap): void {
  const returnUrl = paramMap.get('returnUrl');
  // Retrieves the value of the first query param named 'returnUrl' regardless of case sensitivity
}

Additionally, I've included a helpful getNumber method that simplifies parsing numeric query params. It returns null if the value is not a number or doesn't exist.

Below is the implementation of the class:

case-insensitive-param-map.ts

import { ParamMap, Params } from '@angular/router';

export class CaseInsensitiveParamMap implements ParamMap {
  private params: Params;

  constructor(params: Params) {
    this.params = params || {};
  }

  has(name: string): boolean {
    return Object.keys(this.params).some((key) => key.toLowerCase() === name.toLowerCase());
  }

  private getKeysCaseInsensitively(name: string): string[] {
    return Object.keys(this.params).filter((key) => key.toLowerCase() === name.toLowerCase());
  }

  get(name: string): string | null {
    if (this.has(name)) {
      const keys = this.getKeysCaseInsensitively(name);
      const value = this.params[keys[0]];
      return Array.isArray(value) ? value[0] : value;
    }

    return null;
  }

  getNumber(name: string): number | null {
    const value = this.get(name);
    return !value || isNaN(Number(value)) ? null : Number(value);
  }

  getAll(name: string): string[] {
    if (this.has(name)) {
      const result: string[] = [];
      this.getKeysCaseInsensitively(name).forEach((key) => {
        const value = this.params[key];
        result.push(...(Array.isArray(value) ? value : [value]));
      });
      return result;
    }

    return [];
  }

  get keys(): string[] {
    return Object.keys(this.params);
  }
}

For testing purposes and documentation, here are the accompanying tests:

case-insensitive-param-map.spec.ts

import { Params } from '@angular/router';
import { CaseInsensitiveParamMap } from './case-insensitive-param-map';

describe('CaseInsensitiveParamMap', () => {
  // Test cases go here...
});

Answer №3

If you want a component to display a specific abc item, it's recommended to structure your URLs without query parameters and instead use Routes.

For example, rather than having to display the abc item with an id of 10, you should have a route like

This route would be added as:

 { path: 'abc/:id', component: abcComponent },

where abcComponent is the component responsible for displaying the corresponding item

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

Utilizing the onscroll feature to trigger a function within a ScrollView

I am facing an issue with an animated view where I need to execute multiple events within the onScroll property. The current onScroll implementation is as follows: onScroll={ Animated.event( [{ nativeEvent: { conten ...

Uncover the mystery behind the return value of a generic function in TypeScript

I can't seem to wrap my head around why TypeScript is behaving in the way described below. Snippet 01| const dictionary: { [key: string]: unknown} = {} 02| 03| function set<T>(key: string, value: T): void { 04| dictionary[key] = value; 05| } ...

Angular Material 2 Stepper Controls for Angular applications

I successfully implemented a linear stepper using the Angular Material 2 Stepper component. My project consists of forms in various components (component-a, component-b, component-c). I need the linear stepper in my main container component (container-com ...

Websites experiencing horizontal scrolling functionalities

I have noticed that in my angular project, the website becomes horizontally scrollable when I log in. This only happens after logging in, and does not occur beforehand. I'm using angular calendars and Bootstrap for styling. What could be causing this ...

Angular's let- directive does not assign a local variable

Having trouble setting a local variable within an <ng-template>, as it seems that let- is not functioning properly. Check out the demo here <ng-container *ngIf="data as d"> <ng-container *ngIf="false; else testBlock"></ng-containe ...

Turn off slider trace animation?

Check out the slider component in MUI here: https://mui.com/material-ui/react-slider/ I'm currently exploring how to disable the animation on the nub so it moves instantly to the new position. Any advice on how to achieve this? ...

Is it possible for me to create a CSS class based on a condition using [ngCLASS]?

I am struggling with logic writing in my Angular2 project. On a HTML page, I have two buttons - YES and NO that I want to style with different colors. I have set up a condition in the div tag like this: ngClass="'result'?'yes':' ...

What is the significance of using the @Input decorator on a component property in Angular?

Within this particular component, I discovered the @Input decorator connected to the variable. @Input() description: string; The variable "description" is then utilized in the HTML as an interpolation. <div>{{description}}</div> This prompt ...

Issue with mat-selection-list search filter losing selections upon searching

Currently, I am working on incorporating a mat-selection-list that displays a list of characters. The unique feature is the ability to search and filter characters at the top of the list. Everything works smoothly except for one issue - when you select a c ...

TypeScript is still throwing an error even after verifying with the hasOwnProperty

There exists a type similar to the following: export type PathType = | LivingstoneSouthernWhiteFacedOwl | ArakGroundhog | HubsCampaigns | HubsCampaignsItemID | HubsAlgos | HubsAlgosItemID | TartuGecko | HammerfestPonies | TrapaniSnowLeop ...

What is the best way to set a variable as true within a pipeline?

Could someone assist me with a coding issue I'm facing? If the id is null, I need variable x to be true. I am unable to use if and else statements within the pipe. Any guidance would be greatly appreciated. private x = false; private y = false; n ...

Utilizing PrimeNG's p-dataView feature without repetitive FieldSets

Currently, I am utilizing p-dataView and I'm interested in implementing p-fieldset based on the application type. My goal is to prevent the fieldset from being duplicated when multiple instances occur. The scenario below illustrates one such case; how ...

Ensure that dynamic functions are accurately typed within a proxy utilizing TypeScript

I am currently working on a unique function that utilizes a Proxy with a get trap to extract functions from multiple objects. The challenge I am facing is getting TypeScript to recognize these functions at compile time so that I can add them to my interfac ...

Ways to remove redundant code from multiple service calls

Within my DataService, I have set up an observable for various API calls. I am looking to streamline the process by creating a reusable block of code to be used in each HTTP request. export class DataService { constructor( private http: HttpClient, ...

Error message: `Socket.io-client - Invalid TypeError: Expected a function for socket_io_client_1.default`

I have successfully installed socket.io-client in my Angular 5.2 application, but after trying to connect (which has worked flawlessly in the past), I am encountering a strange error. TypeError: socket_io_client_1.default is not a function at new Auth ...

NGRX: Issue with http retry interceptor preventing failure action from triggering

Incorporating NGRX into my project, I am looking to implement simple GET requests to an API that are retried up to five times. The reason behind this is occasional throttling from Azure Cosmos-DB (free-tier). To achieve this, I have set up an http-interce ...

What is the process for exporting a class to a module and then importing it into another module using TypeScript within an Angular environment?

I have a class called IGeneric that is exported to module A and imported into module B. However, I am unable to use this exported class in module B. Please note that the exported class is not a component, directive, or service; it is a plain TypeScript cl ...

Creating a type in TypeScript with keys as values taken from another type

There is an object const navigatorNames: NavigatorNamesType = { homeNavigation: 'HomeNavigation', authNavigation: 'AuthNavigation' } with the type defined as type NavigatorNamesType = { homeNavigation: 'HomeNavigation ...

The SideNav SpyOn feature failed to locate the specified method

In the test project I am working on, there is a side navigation menu. I need to create a simple test to verify that when I click the button, the sidenav opens or closes. The AppComponent interacts with the sidebar through its dependency, sidenavbar. it(&a ...

What is a more precise way to define an Object type on a temporary basis?

I'm currently working with Angular 2. Typically, when I specify a type, I start by creating an interface: interface Product { name: string, count: number } and then use it like this: let product: Product; However, now I need to temporarily de ...