Angular 4.3 - HttpClient set params
Asked Answered
J

17

114
let httpParams = new HttpParams().set('aaa', '111');
httpParams.set('bbb', '222');

Why this doesn't work? It only set the 'aaa' and NOT the 'bbb'

Also, I have an object { aaa: 111, bbb: 222 } How can I set all the values without looping?

UPDATE (this seems to work, but how can avoid the loop?)

let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
     httpParams = httpParams.append(key, data[key]);
});
Jilly answered 20/7, 2017 at 9:16 Comment(3)
I agree with you that httpParams.set('bbb', '222'); should work. I tried that first and was very confused. But replace that line with httpParams = httpParams.set('bbb','222'); works. for those who are only setting 2, the chaining answer from another User below is also nice.Rosenblatt
just use assignment (=) as @AngelaPan has suggested and you don't have to use loop. Also read about mutable and immutable.Blacking
please vote for the conditional HttpParams set update feature: github.com/angular/angular/issues/26021Karyogamy
J
108

Before 5.0.0-beta.6

let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
     httpParams = httpParams.append(key, data[key]);
});

Since 5.0.0-beta.6

Since 5.0.0-beta.6 (2017-09-03) they added new feature (accept object map for HttpClient headers & params)

Going forward the object can be passed directly instead of HttpParams.

getCountries(data: any) {
    // We don't need any more these lines
    // let httpParams = new HttpParams();
    // Object.keys(data).forEach(function (key) {
    //     httpParams = httpParams.append(key, data[key]);
    // });

    return this.httpClient.get("/api/countries", {params: data})
}
Jilly answered 1/8, 2017 at 4:22 Comment(4)
I'm using 5.2.6 and it definitely won't take a dictionary for the params piece unless I explicitly case it to any. Is that really the intended use?Plasmo
@Plasmo - you can just cast your object to any { params: <any>params } to avoid ts compiler issuesCense
Params of type string that are null will return "null" and Doesn't Support Number, Boolean and DateCondon
Here is a link to the bug report for the lack of Number, Boolean and Date support: github.com/angular/angular/issues/23856Phlebotomize
N
108

HttpParams is intended to be immutable. The set and append methods don't modify the existing instance. Instead they return new instances, with the changes applied.

let params = new HttpParams().set('aaa', 'A');    // now it has aaa
params = params.set('bbb', 'B');                  // now it has both

This approach works well with method chaining:

const params = new HttpParams()
  .set('one', '1')
  .set('two', '2');

...though that might be awkward if you need to wrap any of them in conditions.

Your loop works because you're grabbing a reference to the returned new instance. The code you posted that doesn't work, doesn't. It just calls set() but doesn't grab the result.

let httpParams = new HttpParams().set('aaa', '111'); // now it has aaa
httpParams.set('bbb', '222');                        // result has both but is discarded
Noe answered 31/7, 2017 at 23:35 Comment(1)
How bizarre the choice of an inmutable HttpParams, this was the blocking point and should be the accepted answerDefraud
J
108

Before 5.0.0-beta.6

let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
     httpParams = httpParams.append(key, data[key]);
});

Since 5.0.0-beta.6

Since 5.0.0-beta.6 (2017-09-03) they added new feature (accept object map for HttpClient headers & params)

Going forward the object can be passed directly instead of HttpParams.

getCountries(data: any) {
    // We don't need any more these lines
    // let httpParams = new HttpParams();
    // Object.keys(data).forEach(function (key) {
    //     httpParams = httpParams.append(key, data[key]);
    // });

    return this.httpClient.get("/api/countries", {params: data})
}
Jilly answered 1/8, 2017 at 4:22 Comment(4)
I'm using 5.2.6 and it definitely won't take a dictionary for the params piece unless I explicitly case it to any. Is that really the intended use?Plasmo
@Plasmo - you can just cast your object to any { params: <any>params } to avoid ts compiler issuesCense
Params of type string that are null will return "null" and Doesn't Support Number, Boolean and DateCondon
Here is a link to the bug report for the lack of Number, Boolean and Date support: github.com/angular/angular/issues/23856Phlebotomize
R
58

In more recent versions of @angular/common/http (5.0 and up, by the looks of it), you can use the fromObject key of HttpParamsOptions to pass the object straight in:

let httpParams = new HttpParams({ fromObject: { aaa: 111, bbb: 222 } });

This just runs a forEach loop under the hood, though:

this.map = new Map<string, string[]>();
Object.keys(options.fromObject).forEach(key => {
  const value = (options.fromObject as any)[key];
  this.map !.set(key, Array.isArray(value) ? value : [value]);
});
Rabinowitz answered 21/12, 2017 at 15:48 Comment(12)
why are you saying fromObject? It can be any object.Amy
@ChristianMatthew the constructor of HttpParams can't take "any object", it takes HttpParamsOptions. If you mean you can pass any object as params to a request: yes, but only from v5. Otherwise I'm not sure what you're asking.Rabinowitz
the word fromObject << what is that properlyAmy
@ChristianMatthew I don't know what you're asking. Do you want to know what that property is? Did you look at the API docs? Or the example in the answer?Rabinowitz
You use the fromObject as if it is official. It is just anObject meaning fromObject. Lol maybe what I am asking is confusing. Question, did you make up the word fromObject. That's all I want to knowAmy
@ChristianMatthew what? If I'd just unilaterally made it up, this wouldn't work at all. It also wouldn't be listed in the official docs or source code, both of which I've provided links to.Rabinowitz
@ChristianMatthew you can see HttpParamsOptions used in the HttpParams docs I linked in my first comment. You can see the definition of that in the source code, linked in the answer. It will also be autocompleted in your IDE.Rabinowitz
lol that's just it. when I search and read the docs I don't see fromObjectAmy
@ChristianMatthew again, you can see that in the source; as you can see from the HttpParams page (or using the search) HttpParamsOptions isn't included in the API docs.Rabinowitz
what api version are you looking at?Amy
@ChristianMatthew I don't know what you're asking. What do you mean "API version". Angular version? You can see which commit I've linked to in the answer and I said explicitly it was introduced in 5.0. You can also go and see it in master right now: github.com/angular/angular/blob/master/packages/common/http/src/…. It's not clear to me what problem you have.Rabinowitz
sorry thank you. No its just not in the docs so I was trying to figure out what it does exactly. I see it is there in the source code as you showedAmy
P
31

Couple of Easy Alternatives

Without using HttpParams Objects

let body = {
   params : {
    'email' : emailId,
    'password' : password
   }
}

this.http.post(url, body);

Using HttpParams Objects

let body = new HttpParams({
  fromObject : {
    'email' : emailId,
    'password' : password
  }
})

this.http.post(url, body);
Pearlene answered 27/6, 2018 at 12:11 Comment(3)
This should be the accepted answer. Simple and clean.Schlep
How do you use this with typescript ? If I have a "AuthType" typescript interface with email/password, when using fromObject with this interface it complains that the wrong type is used ? new HttpParams({ fromObject: typedObject } generates a warning "Type YourCustomType is not assignable to type { [param: string]: string | readonly string[] }Humpbacked
Ah, I found out that using new HttpParams({ fromObject: { ...typedObject } } is a way to circumvent the problem.Humpbacked
O
23

As for me, chaining set methods is the cleanest way

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
O answered 26/7, 2017 at 14:32 Comment(2)
HttpParams is immutable like HttpHeaders, which means that the second .set() call will not work in this case. It needs to be set in one hit or goes with the .append() method assigning the output to a new variable as the OP suggested on his update.Squalene
@Squalene I just tried Mike's chaining set. and it does work! I think it's nice for people that only has 2 parameters. It's easy to understand.Rosenblatt
S
18

Another way to do it is:

this.httpClient.get('path', {
    params: Object.entries(data).reduce((params, [key, value]) => params.set(key, value), new HttpParams());
});
Salamander answered 6/9, 2017 at 10:47 Comment(2)
You can add a polyfill (#42410529)Salamander
gonna have to try this wizardryAmy
P
9

Since HTTP Params class is immutable therefore you need to chain the set method:

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
Pellerin answered 16/5, 2018 at 13:21 Comment(0)
K
7

Using this you can avoid the loop.

// obj is the params object with key-value pair. 
// This is how you convert that to HttpParams and pass this to GET API. 

const params = Object.getOwnPropertyNames(obj)
               .reduce((p, key) => p.set(key, obj[key]), new HttpParams());

Furthermore, I suggest making toHttpParams function in your commonly used service. So you can call the function to convert the object to the HttpParams.

/**
 * Convert Object to HttpParams
 * @param {Object} obj
 * @returns {HttpParams}
 */
toHttpParams(obj: Object): HttpParams {
    return Object.getOwnPropertyNames(obj)
        .reduce((p, key) => p.set(key, obj[key]), new HttpParams());
}

Update:

Since 5.0.0-beta.6 (2017-09-03) they added new feature (accept object map for HttpClient headers & params)

Going forward the object can be passed directly instead of HttpParams.

This is the other reason if you have used one common function like toHttpParams mentioned above, you can easily remove it or do changes if required.

Kelcie answered 9/11, 2017 at 21:34 Comment(1)
Hi its working fine but append extra undefined key value as paramMinicam
A
3

As far as I can see from the implementation at https://github.com/angular/angular/blob/master/packages/common/http/src/params.ts

You must provide values separately - You are not able to avoid your loop.

There is also a constructor which takes a string as a parameter, but it is in form param=value&param2=value2 so there is no deal for You (in both cases you will finish with looping your object).

You can always report an issue/feature request to angular, what I strongly advise: https://github.com/angular/angular/issues

PS: Remember about difference between set and append methods ;)

Antistrophe answered 20/7, 2017 at 10:19 Comment(1)
what are the differences?Amy
S
3

appendAll function

import { HttpParams } from "@angular/common/http";

export class BuildUrl {

    /**
     * append all params
     * @param args {unknown[]}
     * @returns {HttpParams}
     */
    static appendAll(...args: unknown[]): HttpParams {
        let params = new HttpParams();
        args.forEach(param => {
            Object.keys(param).forEach((key) => {
                params = params.append(key, param[key]);
            });
        });
        return params;
    }
}

how we use it in the service.

getData( pag: PaginationRequest, description: string = ''): Observable<any> {
    const params = BuildUrl.appendAll(pag,
      { description },
    );
    return this.http.get(url, { params });
  }
Stubblefield answered 11/3, 2021 at 14:43 Comment(0)
J
2

Since @MaciejTreder confirmed that we have to loop, here's a wrapper that will optionally let you add to a set of default params:

    function genParams(params: object, httpParams = new HttpParams()): object {
        Object.keys(params)
            .filter(key => {
                let v = params[key];
                return (Array.isArray(v) || typeof v === 'string') ? 
                    (v.length > 0) : 
                    (v !== null && v !== undefined);
            })
            .forEach(key => {
                httpParams = httpParams.set(key, params[key]);
            });
        return { params: httpParams };
    }

You can use it like so:

    const OPTIONS = {
        headers: new HttpHeaders({
            'Content-Type': 'application/json'
        }),
        params: new HttpParams().set('verbose', 'true')
    };
    let opts = Object.assign({}, OPTIONS, genParams({ id: 1 }, OPTIONS.params));
    this.http.get(BASE_URL, opts); // --> ...?verbose=true&id=1
Jiggerypokery answered 16/8, 2017 at 20:31 Comment(0)
M
2

My helper class (ts) to convert any complex dto object (not only "string dictionary") to HttpParams:

import { HttpParams } from "@angular/common/http";

export class HttpHelper {
  static toHttpParams(obj: any): HttpParams {
    return this.addToHttpParams(new HttpParams(), obj, null);
  }

  private static addToHttpParams(params: HttpParams, obj: any, prefix: string): HttpParams {    
    for (const p in obj) {
      if (obj.hasOwnProperty(p)) {
        var k = p;
        if (prefix) {
          if (p.match(/^-{0,1}\d+$/)) {
            k = prefix + "[" + p + "]";
          } else {
            k = prefix + "." + p;
          }
        }        
        var v = obj[p];
        if (v !== null && typeof v === "object" && !(v instanceof Date)) {
          params = this.addToHttpParams(params, v, k);
        } else if (v !== undefined) {
          if (v instanceof Date) {
            params = params.set(k, (v as Date).toISOString()); //serialize date as you want
          }
          else {
            params = params.set(k, v);
          }

        }
      }
    }
    return params;
  }
}

console.info(
  HttpHelper.toHttpParams({
    id: 10,
    date: new Date(),
    states: [1, 3],
    child: {
      code: "111"
    }
  }).toString()
); // id=10&date=2019-08-02T13:19:09.014Z&states%5B0%5D=1&states%5B1%5D=3&child.code=111
Misdemean answered 2/8, 2019 at 13:25 Comment(1)
You really don't need this, the Angular params.ts has its own encoding. Check the implementation: github.com/angular/angular/blob/master/packages/common/http/src/…Watercolor
C
2

Just wanted to add that if you want to add several parameters with the same key name for example: www.test.com/home?id=1&id=2

let params = new HttpParams();
params = params.append(key, value);

Use append, if you use set, it will overwrite the previous value with the same key name.

Cockswain answered 9/6, 2020 at 12:55 Comment(0)
M
1

This solutions working for me,

let params = new HttpParams(); Object.keys(data).forEach(p => { params = params.append(p.toString(), data[p].toString()); });

Materialism answered 27/6, 2019 at 19:15 Comment(0)
E
1

Angular 14

This method works well on Angular 14. Passing parameters to the backend using HttpParams

get<T>(path: string): Observable<T> {
    let httpParams = new HttpParams();
    httpParams = httpParams.append("language", "en");
    httpParams = httpParams.append("pageNo", 1);
    httpParams = httpParams.append("pageSize", 10);

    const options = { params: httpParams };

    return this.http.get<T>(`${this.apiUrl}/${path}`, options);
}

// T is a generic type returned by the `get` method.
Everett answered 6/12, 2022 at 22:46 Comment(0)
P
0

Using set()

From Angular Official Documentation of set()

set(param: string, value: string | number | boolean): HttpParams

Returns HttpParams: A new body with the new value.

Now, lets see how we can add some parameters -

const params = new HttpParams()
    .set('aaa', '111')
    .set('bbb', "222");

Notice that we are constructing the HTTPParams object by chaining set() methods. The reason is HTTPParams is immutable.

Every time a call to set method will return a new HttpParams object with the new value. We need to first grab the returned HttpParams object and then use that for further modification.

So, If we do the following, it will not work

const params = new HttpParams(); // empty HttpParams object

params.set('aaa', '111'); // lost
params.set('bbb', "222"); // lost

We are only calling set but not keeping the returned HttpParams object. So, lastly we would have an empty HttpParams object, and the two calls to set would have add no effect. To make it work, wee need to do the following -

const params = new HttpParams(); // empty HttpParams object

params = params.set('aaa', '111'); // storing the returned HttpParams object, now **it has aaa**
params = params.set('bbb', "222"); // add another parameter and store it. so now **it has both aaa and bbb**

Using fromString

you can also use fromString to create httpParams. It's good when you already have a Query parameters string prepared. you can do it using the following syntax

const params = new HttpParams({
  fromString: 'aaa=111&bbb=222'
});
Parietal answered 19/8, 2021 at 7:56 Comment(0)
U
-1
 getHttpParams(data: any) {
   let httpParams = new HttpParams();
   Object.keys(data).forEach(key => {
     if (data[key] != null) {
       if (typeof data[key] === "object") {
         var childData = data[key];
         Object.keys(childData).forEach(childKey => {
           httpParams = httpParams.append(childKey, childData[childKey]);
         });
       } else {
         httpParams = httpParams.append(key, data[key]);
       }
     }
   });
   return httpParams;
 }
Unpopular answered 28/11, 2023 at 17:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.