fix(common): allow number or boolean as http params (#40663)
This change fixes an incompatibility between the old `@angular/http` package
and its successor (`@angular/common/http`) by re-introducing the types that were supported before.
It now allows to use number and boolean directly as HTTP params, instead of having to convert it to string first.
Before:
this.http.get('/api/config', { params: { page: `${page}` } });
After:
this.http.get('/api/config', { params: { page }});
`HttpParams` has also been updated to have most of its methods accept number or boolean values.
Fixes #23856
BREAKING CHANGE:
The methods of the `HttpParams` class now accept `string | number | boolean`
instead of `string` for the value of a parameter.
If you extended this class in your application,
you'll have to update the signatures of your methods to reflect these changes.
PR Close #40663
This commit is contained in:
@@ -72,7 +72,7 @@ The `get()` method takes two arguments; the endpoint URL from which to fetch, an
|
||||
options: {
|
||||
headers?: HttpHeaders | {[header: string]: string | string[]},
|
||||
observe?: 'body' | 'events' | 'response',
|
||||
params?: HttpParams|{[param: string]: string | string[]},
|
||||
params?: HttpParams|{[param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>},
|
||||
reportProgress?: boolean,
|
||||
responseType?: 'arraybuffer'|'blob'|'json'|'text',
|
||||
withCredentials?: boolean,
|
||||
|
||||
+126
-126
File diff suppressed because it is too large
Load Diff
+261
-130
File diff suppressed because it is too large
Load Diff
@@ -101,10 +101,13 @@ function standardEncoding(v: string): string {
|
||||
.replace(/%3F/gi, '?')
|
||||
.replace(/%2F/gi, '/');
|
||||
}
|
||||
function valueToString(value: string|number|boolean): string {
|
||||
return `${value}`;
|
||||
}
|
||||
|
||||
interface Update {
|
||||
param: string;
|
||||
value?: string;
|
||||
value?: string|number|boolean;
|
||||
op: 'a'|'d'|'s';
|
||||
}
|
||||
|
||||
@@ -121,7 +124,7 @@ export interface HttpParamsOptions {
|
||||
fromString?: string;
|
||||
|
||||
/** Object map of the HTTP parameters. Mutually exclusive with `fromString`. */
|
||||
fromObject?: {[param: string]: string|ReadonlyArray<string>};
|
||||
fromObject?: {[param: string]: string|number|boolean|ReadonlyArray<string|number|boolean>};
|
||||
|
||||
/** Encoding codec used to parse and serialize the parameters. */
|
||||
encoder?: HttpParameterCodec;
|
||||
@@ -208,7 +211,7 @@ export class HttpParams {
|
||||
* @param value The new value to add.
|
||||
* @return A new body with the appended value.
|
||||
*/
|
||||
append(param: string, value: string): HttpParams {
|
||||
append(param: string, value: string|number|boolean): HttpParams {
|
||||
return this.clone({param, value, op: 'a'});
|
||||
}
|
||||
|
||||
@@ -217,7 +220,8 @@ export class HttpParams {
|
||||
* @param params parameters and values
|
||||
* @return A new body with the new value.
|
||||
*/
|
||||
appendAll(params: {[param: string]: string|string[]}): HttpParams {
|
||||
appendAll(params: {[param: string]: string|number|boolean|ReadonlyArray<string|number|boolean>}):
|
||||
HttpParams {
|
||||
const updates: Update[] = [];
|
||||
Object.keys(params).forEach(param => {
|
||||
const value = params[param];
|
||||
@@ -226,7 +230,7 @@ export class HttpParams {
|
||||
updates.push({param, value: _value, op: 'a'});
|
||||
});
|
||||
} else {
|
||||
updates.push({param, value, op: 'a'});
|
||||
updates.push({param, value: value as (string | number | boolean), op: 'a'});
|
||||
}
|
||||
});
|
||||
return this.clone(updates);
|
||||
@@ -238,7 +242,7 @@ export class HttpParams {
|
||||
* @param value The new value.
|
||||
* @return A new body with the new value.
|
||||
*/
|
||||
set(param: string, value: string): HttpParams {
|
||||
set(param: string, value: string|number|boolean): HttpParams {
|
||||
return this.clone({param, value, op: 's'});
|
||||
}
|
||||
|
||||
@@ -249,7 +253,7 @@ export class HttpParams {
|
||||
* @return A new body with the given value removed, or with all values
|
||||
* removed if no value is specified.
|
||||
*/
|
||||
delete(param: string, value?: string): HttpParams {
|
||||
delete(param: string, value?: string|number|boolean): HttpParams {
|
||||
return this.clone({param, value, op: 'd'});
|
||||
}
|
||||
|
||||
@@ -293,13 +297,13 @@ export class HttpParams {
|
||||
case 'a':
|
||||
case 's':
|
||||
const base = (update.op === 'a' ? this.map!.get(update.param) : undefined) || [];
|
||||
base.push(update.value!);
|
||||
base.push(valueToString(update.value!));
|
||||
this.map!.set(update.param, base);
|
||||
break;
|
||||
case 'd':
|
||||
if (update.value !== undefined) {
|
||||
let base = this.map!.get(update.param) || [];
|
||||
const idx = base.indexOf(update.value);
|
||||
const idx = base.indexOf(valueToString(update.value));
|
||||
if (idx !== -1) {
|
||||
base.splice(idx, 1);
|
||||
}
|
||||
|
||||
@@ -51,10 +51,34 @@ import {toArray} from 'rxjs/operators';
|
||||
expect(req.request.headers.get('X-Option')).toEqual('true');
|
||||
req.flush({});
|
||||
});
|
||||
it('with params', done => {
|
||||
it('with string params', done => {
|
||||
client.get('/test', {params: {'test': 'true'}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=true').flush({});
|
||||
});
|
||||
it('with an array of string params', done => {
|
||||
client.get('/test', {params: {'test': ['a', 'b']}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=a&test=b').flush({});
|
||||
});
|
||||
it('with number params', done => {
|
||||
client.get('/test', {params: {'test': 2}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=2').flush({});
|
||||
});
|
||||
it('with an array of number params', done => {
|
||||
client.get('/test', {params: {'test': [2, 3]}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=2&test=3').flush({});
|
||||
});
|
||||
it('with boolean params', done => {
|
||||
client.get('/test', {params: {'test': true}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=true').flush({});
|
||||
});
|
||||
it('with an array of boolean params', done => {
|
||||
client.get('/test', {params: {'test': [true, false]}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=true&test=false').flush({});
|
||||
});
|
||||
it('with an array of params of different types', done => {
|
||||
client.get('/test', {params: {'test': [true, 'a', 2] as const}}).subscribe(() => done());
|
||||
backend.expectOne('/test?test=true&test=a&test=2').flush({});
|
||||
});
|
||||
it('for an arraybuffer', done => {
|
||||
const body = new ArrayBuffer(4);
|
||||
client.get('/test', {responseType: 'arraybuffer'}).subscribe(res => {
|
||||
|
||||
@@ -37,42 +37,120 @@ import {HttpParams} from '@angular/common/http/src/params';
|
||||
});
|
||||
|
||||
describe('lazy mutation', () => {
|
||||
it('should allow setting parameters', () => {
|
||||
it('should allow setting string parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.set('a', 'c');
|
||||
expect(mutated.toString()).toEqual('a=c');
|
||||
});
|
||||
|
||||
it('should allow appending parameters', () => {
|
||||
it('should allow setting number parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.set('a', 1);
|
||||
expect(mutated.toString()).toEqual('a=1');
|
||||
});
|
||||
|
||||
it('should allow setting boolean parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.set('a', true);
|
||||
expect(mutated.toString()).toEqual('a=true');
|
||||
});
|
||||
|
||||
it('should allow appending string parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.append('a', 'c');
|
||||
expect(mutated.toString()).toEqual('a=b&a=c');
|
||||
});
|
||||
|
||||
it('should allow appending all parameters', () => {
|
||||
it('should allow appending number parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.append('a', 1);
|
||||
expect(mutated.toString()).toEqual('a=b&a=1');
|
||||
});
|
||||
|
||||
it('should allow appending boolean parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.append('a', true);
|
||||
expect(mutated.toString()).toEqual('a=b&a=true');
|
||||
});
|
||||
|
||||
it('should allow appending all string parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=a1&b=b1'});
|
||||
const mutated = body.appendAll({a: ['a2', 'a3'], b: 'b2'});
|
||||
expect(mutated.toString()).toEqual('a=a1&a=a2&a=a3&b=b1&b=b2');
|
||||
});
|
||||
|
||||
it('should allow appending all number parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=1&b=b1'});
|
||||
const mutated = body.appendAll({a: [2, 3], b: 'b2'});
|
||||
expect(mutated.toString()).toEqual('a=1&a=2&a=3&b=b1&b=b2');
|
||||
});
|
||||
|
||||
it('should allow appending all boolean parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=true&b=b1'});
|
||||
const mutated = body.appendAll({a: [true, false], b: 'b2'});
|
||||
expect(mutated.toString()).toEqual('a=true&a=true&a=false&b=b1&b=b2');
|
||||
});
|
||||
|
||||
it('should allow appending all parameters of different types', () => {
|
||||
const body = new HttpParams({fromString: 'a=true&b=b1'});
|
||||
const mutated = body.appendAll({a: [true, 0, 'a1'] as const, b: 'b2'});
|
||||
expect(mutated.toString()).toEqual('a=true&a=true&a=0&a=a1&b=b1&b=b2');
|
||||
});
|
||||
|
||||
it('should allow deletion of parameters', () => {
|
||||
const body = new HttpParams({fromString: 'a=b&c=d&e=f'});
|
||||
const mutated = body.delete('c');
|
||||
expect(mutated.toString()).toEqual('a=b&e=f');
|
||||
});
|
||||
|
||||
it('should allow deletion of parameters with specific string value', () => {
|
||||
const body = new HttpParams({fromString: 'a=b&c=d&e=f'});
|
||||
const notMutated = body.delete('c', 'z');
|
||||
expect(notMutated.toString()).toEqual('a=b&c=d&e=f');
|
||||
const mutated = body.delete('c', 'd');
|
||||
expect(mutated.toString()).toEqual('a=b&e=f');
|
||||
});
|
||||
|
||||
it('should allow deletion of parameters with specific number value', () => {
|
||||
const body = new HttpParams({fromString: 'a=b&c=1&e=f'});
|
||||
const notMutated = body.delete('c', 2);
|
||||
expect(notMutated.toString()).toEqual('a=b&c=1&e=f');
|
||||
const mutated = body.delete('c', 1);
|
||||
expect(mutated.toString()).toEqual('a=b&e=f');
|
||||
});
|
||||
|
||||
it('should allow deletion of parameters with specific boolean value', () => {
|
||||
const body = new HttpParams({fromString: 'a=b&c=true&e=f'});
|
||||
const notMutated = body.delete('c', false);
|
||||
expect(notMutated.toString()).toEqual('a=b&c=true&e=f');
|
||||
const mutated = body.delete('c', true);
|
||||
expect(mutated.toString()).toEqual('a=b&e=f');
|
||||
});
|
||||
|
||||
it('should allow chaining of mutations', () => {
|
||||
const body = new HttpParams({fromString: 'a=b&c=d&e=f'});
|
||||
const mutated = body.append('e', 'y').delete('c').set('a', 'x').append('e', 'z');
|
||||
expect(mutated.toString()).toEqual('a=x&e=f&e=y&e=z');
|
||||
});
|
||||
|
||||
it('should allow deletion of one value of a parameter', () => {
|
||||
it('should allow deletion of one value of a string parameter', () => {
|
||||
const body = new HttpParams({fromString: 'a=1&a=2&a=3&a=4&a=5'});
|
||||
const mutated = body.delete('a', '2').delete('a', '4');
|
||||
expect(mutated.getAll('a')).toEqual(['1', '3', '5']);
|
||||
});
|
||||
|
||||
it('should allow deletion of one value of a number parameter', () => {
|
||||
const body = new HttpParams({fromString: 'a=0&a=1&a=2&a=3&a=4&a=5'});
|
||||
const mutated = body.delete('a', 0).delete('a', 4);
|
||||
expect(mutated.getAll('a')).toEqual(['1', '2', '3', '5']);
|
||||
});
|
||||
|
||||
it('should allow deletion of one value of a boolean parameter', () => {
|
||||
const body = new HttpParams({fromString: 'a=false&a=true&a=false'});
|
||||
const mutated = body.delete('a', false);
|
||||
expect(mutated.getAll('a')).toEqual(['true', 'false']);
|
||||
});
|
||||
|
||||
it('should not repeat mutations that have already been materialized', () => {
|
||||
const body = new HttpParams({fromString: 'a=b'});
|
||||
const mutated = body.append('a', 'c');
|
||||
@@ -101,10 +179,30 @@ import {HttpParams} from '@angular/common/http/src/params';
|
||||
const body = new HttpParams({fromObject: {a: '', b: '2', c: '3'}});
|
||||
expect(body.toString()).toBe('a=&b=2&c=3');
|
||||
});
|
||||
it('should stringify array params', () => {
|
||||
it('should stringify string array params', () => {
|
||||
const body = new HttpParams({fromObject: {a: '', b: ['21', '22'], c: '3'}});
|
||||
expect(body.toString()).toBe('a=&b=21&b=22&c=3');
|
||||
});
|
||||
it('should stringify number params', () => {
|
||||
const body = new HttpParams({fromObject: {a: '', b: 2, c: 3}});
|
||||
expect(body.toString()).toBe('a=&b=2&c=3');
|
||||
});
|
||||
it('should stringify number array params', () => {
|
||||
const body = new HttpParams({fromObject: {a: '', b: [21, 22], c: 3}});
|
||||
expect(body.toString()).toBe('a=&b=21&b=22&c=3');
|
||||
});
|
||||
it('should stringify boolean params', () => {
|
||||
const body = new HttpParams({fromObject: {a: '', b: true, c: 3}});
|
||||
expect(body.toString()).toBe('a=&b=true&c=3');
|
||||
});
|
||||
it('should stringify boolean array params', () => {
|
||||
const body = new HttpParams({fromObject: {a: '', b: [true, false], c: 3}});
|
||||
expect(body.toString()).toBe('a=&b=true&b=false&c=3');
|
||||
});
|
||||
it('should stringify array params of different types', () => {
|
||||
const body = new HttpParams({fromObject: {a: ['', false, 3] as const}});
|
||||
expect(body.toString()).toBe('a=&a=false&a=3');
|
||||
});
|
||||
it('should stringify empty array params', () => {
|
||||
const body = new HttpParams({fromObject: {a: '', b: [], c: '3'}});
|
||||
expect(body.toString()).toBe('a=&c=3');
|
||||
|
||||
Reference in New Issue
Block a user