diff --git a/modules/@angular/http/src/base_request_options.ts b/modules/@angular/http/src/base_request_options.ts index 5409a44d91..b844a6c308 100644 --- a/modules/@angular/http/src/base_request_options.ts +++ b/modules/@angular/http/src/base_request_options.ts @@ -62,7 +62,15 @@ export class RequestOptions { /** * Search parameters to be included in a {@link Request}. */ - search: URLSearchParams; + params: URLSearchParams; + /** + * @deprecated from 4.0.0. Use params instead. + */ + get search(): URLSearchParams { return this.params; } + /** + * @deprecated from 4.0.0. Use params instead. + */ + set search(params: URLSearchParams) { this.params = params; } /** * Enable use credentials for a {@link Request}. */ @@ -72,15 +80,15 @@ export class RequestOptions { */ responseType: ResponseContentType; + // TODO(Dzmitry): remove search when this.search is removed constructor( - {method, headers, body, url, search, withCredentials, + {method, headers, body, url, search, params, withCredentials, responseType}: RequestOptionsArgs = {}) { this.method = method != null ? normalizeMethodName(method) : null; this.headers = headers != null ? headers : null; this.body = body != null ? body : null; this.url = url != null ? url : null; - this.search = - search != null ? (typeof search === 'string' ? new URLSearchParams(search) : search) : null; + this.params = this._mergeSearchParams(params || search); this.withCredentials = withCredentials != null ? withCredentials : null; this.responseType = responseType != null ? responseType : null; } @@ -116,18 +124,49 @@ export class RequestOptions { headers: options && options.headers != null ? options.headers : this.headers, body: options && options.body != null ? options.body : this.body, url: options && options.url != null ? options.url : this.url, - search: options && options.search != null ? - (typeof options.search === 'string' ? new URLSearchParams(options.search) : - options.search.clone()) : - this.search, + params: options && this._mergeSearchParams(options.params || options.search), withCredentials: options && options.withCredentials != null ? options.withCredentials : this.withCredentials, responseType: options && options.responseType != null ? options.responseType : this.responseType }); } -} + private _mergeSearchParams(params: string|URLSearchParams| + {[key: string]: any | any[]}): URLSearchParams { + if (!params) return this.params; + + if (params instanceof URLSearchParams) { + return params.clone(); + } + + if (typeof params === 'string') { + return new URLSearchParams(params); + } + + return this._parseParams(params); + } + + private _parseParams(objParams: {[key: string]: any | any[]} = {}): URLSearchParams { + const params = new URLSearchParams(); + Object.keys(objParams).forEach((key: string) => { + const value: any|any[] = objParams[key]; + if (Array.isArray(value)) { + value.forEach((item: any) => this._appendParam(key, item, params)); + } else { + this._appendParam(key, value, params); + } + }); + return params; + } + + private _appendParam(key: string, value: any, params: URLSearchParams): void { + if (typeof value !== 'string') { + value = JSON.stringify(value); + } + params.append(key, value); + } +} /** * Subclass of {@link RequestOptions}, with default values. diff --git a/modules/@angular/http/src/interfaces.ts b/modules/@angular/http/src/interfaces.ts index c123265f6b..7155e8ee40 100644 --- a/modules/@angular/http/src/interfaces.ts +++ b/modules/@angular/http/src/interfaces.ts @@ -48,7 +48,9 @@ export abstract class XSRFStrategy { abstract configureRequest(req: Request): vo export interface RequestOptionsArgs { url?: string; method?: string|RequestMethod; - search?: string|URLSearchParams; + /** @deprecated from 4.0.0. Use params instead. */ + search?: string|URLSearchParams|{[key: string]: any | any[]}; + params?: string|URLSearchParams|{[key: string]: any | any[]}; headers?: Headers; body?: any; withCredentials?: boolean; diff --git a/modules/@angular/http/src/static_request.ts b/modules/@angular/http/src/static_request.ts index 9c756a9df8..adace1648f 100644 --- a/modules/@angular/http/src/static_request.ts +++ b/modules/@angular/http/src/static_request.ts @@ -76,15 +76,15 @@ export class Request extends Body { // TODO: assert that url is present const url = requestOptions.url; this.url = requestOptions.url; - if (requestOptions.search) { - const search = requestOptions.search.toString(); - if (search.length > 0) { + if (requestOptions.params) { + const params = requestOptions.params.toString(); + if (params.length > 0) { let prefix = '?'; if (this.url.indexOf('?') != -1) { prefix = (this.url[this.url.length - 1] == '&') ? '' : '&'; } // TODO: just delete search-query-looking string in url? - this.url = url + prefix + search; + this.url = url + prefix + params; } } this._body = requestOptions.body; diff --git a/modules/@angular/http/test/base_request_options_spec.ts b/modules/@angular/http/test/base_request_options_spec.ts index ca4d4e1224..4f70e04fc7 100644 --- a/modules/@angular/http/test/base_request_options_spec.ts +++ b/modules/@angular/http/test/base_request_options_spec.ts @@ -24,5 +24,26 @@ export function main() { const options2 = options1.merge(new RequestOptions({method: RequestMethod.Delete})); expect(options2.method).toBe(RequestMethod.Delete); }); + + it('should accept search params as object', () => { + const params = {a: 1, b: 'text', c: [1, 2, '3']}; + const options = new RequestOptions({params}); + + expect(options.params.paramsMap.size).toBe(3); + expect(options.params.paramsMap.get('a')).toEqual(['1']); + expect(options.params.paramsMap.get('b')).toEqual(['text']); + expect(options.params.paramsMap.get('c')).toEqual(['1', '2', '3']); + }); + + it('should merge search params as object', () => { + const options1 = new BaseRequestOptions(); + const params = {a: 1, b: 'text', c: [1, 2, '3']}; + const options2 = options1.merge(new RequestOptions({params})); + + expect(options2.params.paramsMap.size).toBe(3); + expect(options2.params.paramsMap.get('a')).toEqual(['1']); + expect(options2.params.paramsMap.get('b')).toEqual(['text']); + expect(options2.params.paramsMap.get('c')).toEqual(['1', '2', '3']); + }); }); } diff --git a/tools/public_api_guard/http/index.d.ts b/tools/public_api_guard/http/index.d.ts index 8534db90f5..061a804f05 100644 --- a/tools/public_api_guard/http/index.d.ts +++ b/tools/public_api_guard/http/index.d.ts @@ -139,11 +139,12 @@ export declare class RequestOptions { body: any; headers: Headers; method: RequestMethod | string; + params: URLSearchParams; responseType: ResponseContentType; - search: URLSearchParams; + /** @deprecated */ search: URLSearchParams; url: string; withCredentials: boolean; - constructor({method, headers, body, url, search, withCredentials, responseType}?: RequestOptionsArgs); + constructor({method, headers, body, url, search, params, withCredentials, responseType}?: RequestOptionsArgs); merge(options?: RequestOptionsArgs): RequestOptions; } @@ -152,8 +153,13 @@ export interface RequestOptionsArgs { body?: any; headers?: Headers; method?: string | RequestMethod; + params?: string | URLSearchParams | { + [key: string]: any | any[]; + }; responseType?: ResponseContentType; - search?: string | URLSearchParams; + /** @deprecated */ search?: string | URLSearchParams | { + [key: string]: any | any[]; + }; url?: string; withCredentials?: boolean; }