Reputation: 41
I have a URL with the query string id. But, the variable id can be come as 'id' or 'Id' in the URL.
As per my understanding, these 2 will be treated differently. For handling the following URL's, I have wrote the code as 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']);
}
}
});
}
Other than this, is there any simple way to handle the case sensitive variables and take the values properly in angular4?
Upvotes: 4
Views: 7067
Reputation: 2982
After perusing the internet and not finding a satisfactory solution, I wrote my own re-usable class that wraps the built-in Params
object and provides case-insensitive access to query string parameters.
You can use it in your component like:
import { ActivatedRoute } from '@angular/router';
import { CaseInsensitiveParamMap } from '../case-insensitive-param-map';
constructor(private activatedRoute: ActivatedRoute) {}
ngOnInit(): void {
// subscribe to query params changes:
this.activatedRoute.queryParams.snapshot.paramMap
.subscribe((params) => this.handleQueryParamsChanged(new CaseInsensitiveParamMap(params)));
// or use the 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');
// Returns the value of the first query param named 'returnUrl' case-insensitively
// See the test suite below for more examples
}
Note that I also added a getNumber
helper method that makes it much easier to parse numeric query params. It returns null
if the value is not a number or is not present.
Here is 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 v = this.params[keys[0]];
return Array.isArray(v) ? v[0] : v;
}
return null;
}
getNumber(name: string): number | null {
const v = this.get(name);
return !v || isNaN(Number(v)) ? null : Number(v);
}
getAll(name: string): string[] {
if (this.has(name)) {
const result: string[] = [];
this.getKeysCaseInsensitively(name).forEach((key) => {
const v = this.params[key];
result.push(...(Array.isArray(v) ? v : [v]));
});
return result;
}
return [];
}
get keys(): string[] {
return Object.keys(this.params);
}
}
Here is the tests that outline the behavior and serve as documentation:
case-insensitive-param-map.spec.ts
import { Params } from '@angular/router';
import { CaseInsensitiveParamMap } from './case-insensitive-param-map';
describe('CaseInsensitiveParamMap', () => {
it('should return whether a case-insensitive parameter is present', () => {
const params: Params = { single: 's', multiple: ['m1', 'm2'] };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.has('single')).toEqual(true);
expect(map.has('SINGLE')).toEqual(true);
expect(map.has('multiple')).toEqual(true);
expect(map.has('MULTIPLE')).toEqual(true);
expect(map.has('not here')).toEqual(false);
});
it('should return the keys of the parameters', () => {
const params: Params = { single: 's', multiple: ['m1', 'm2'] };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.keys).toEqual(['single', 'multiple']);
});
it('should support single valued parameters', () => {
const params: Params = { single: 's', multiple: ['m1', 'm2'] };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.get('single')).toEqual('s');
expect(map.get('multiple')).toEqual('m1');
});
it('should get numeric query string values as numbers', () => {
const params: Params = { single: '1', multiple: ['2', '3'], nonnumeric: 'foo', empty: '', zero: '0', nullProp: null, undefinedProp: undefined };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.getNumber('single')).toEqual(1);
expect(map.getNumber('multiple')).toEqual(2);
expect(map.getNumber('zero')).toEqual(0);
expect(map.getNumber('nullProp')).toEqual(null);
expect(map.getNumber('undefinedProp')).toEqual(null);
expect(map.getNumber('nonnumeric')).toEqual(null);
expect(map.getNumber('not here')).toEqual(null);
expect(map.getNumber('empty')).toEqual(null);
});
it('should support case-insensitive single valued parameters', () => {
const params: Params = { SINGLE: 'S', MULTIPLE: ['M1', 'M2'] };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.get('single')).toEqual('S');
expect(map.get('SINGLE')).toEqual('S');
expect(map.get('Single')).toEqual('S');
expect(map.get('multiple')).toEqual('M1');
expect(map.get('MULTIPLE')).toEqual('M1');
expect(map.get('Multiple')).toEqual('M1');
});
it('should support multiple valued parameters', () => {
const params: Params = { single: 's', multiple: ['m1', 'm2'] };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.getAll('single')).toEqual(['s']);
expect(map.getAll('multiple')).toEqual(['m1', 'm2']);
});
it('should support case-insensitive multiple valued parameters', () => {
const params: Params = { SINGLE: 'S', MULTIPLE: ['M1', 'M2'], multiple: ['M3'] };
const map: CaseInsensitiveParamMap = new CaseInsensitiveParamMap(params);
expect(map.getAll('single')).toEqual(['S']);
expect(map.getAll('multiple')).toEqual(['M1', 'M2', 'M3']);
});
it('should return null when a single valued element is absent', () => {
expect(new CaseInsensitiveParamMap({}).get('not here')).toEqual(null);
});
it('should return [] when a multiple valued element is absent', () => {
expect(new CaseInsensitiveParamMap({}).getAll('not here')).toEqual([]);
});
});
Upvotes: 2
Reputation: 18292
As @vogomatix said, you shouldn't have the same param with different names. But, if for whichever reason you do, you can simplfy your code a bit like this:
private findQueryParams() {
this._route.queryParams.subscribe((params: Params) => {
const idValue = params['id'] || params['Id'] || '0';
let id = Number(idValue);
});
}
This way you'll use the value of id
, if not exists, then Id
, and if doesn't exists, either, then you'll use '0'
If there can be more combinations, as you say, then your best option is to clone the params to a lowercase version:
function toLower(params: Params): Params {
const lowerParams: Params = {};
for (const key in params) {
lowerParams[key.toLowerCase()] = params[key];
}
return lowerParams;
}
With this:
private findQueryParams() {
this._route.queryParams.subscribe((params: Params) => {
const _params = toLower(params);
const idValue = params['id'] || '0';
let id = Number(params);
});
}
Of course you've got to this every time you get params from the Router
, but that is unavoidable, as the base defect is in the Url system.
Upvotes: 11
Reputation: 5041
If you wish to have a component which shows a specific abc
item, then you should really be having your URLs without query parameters and embracing the concept of Routes instead
i.e. instead of having http://xxx/abc?id=10 to show the abc
item with an id of 10 you should just be having a route http://xxx/abc/10
You would then add this route as:
{ path: 'abc/:id', component: abcComponent },
where abcComponent is the component which shows the relevant item
Upvotes: 0