Angular 4.3 - HttpClient set params

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

¿Por qué esto no funciona? Solo establece el 'aaa' y NO el 'bbb'

Además, tengo un objeto {aaa: 111, bbb: 222} ¿Cómo puedo establecer todos los valores sin bucles?

ACTUALIZAR (esto parece funcionar, pero ¿cómo se puede evitar el bucle?)

let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
     httpParams = httpParams.append(key, data[key]);
});
Michalis
fuente
3
Estoy de acuerdo contigo en que httpParams.set('bbb', '222');debería funcionar. Probé eso primero y estaba muy confundido. Pero reemplace esa línea con httpParams = httpParams.set('bbb','222');obras. para aquellos que solo están configurando 2, la respuesta de encadenamiento de otro usuario a continuación también es agradable.
Angela P
1
simplemente use la asignación (=) como @AngelaPan ha sugerido y no tiene que usar el bucle. Lea también sobre mutable e inmutable.
Junaid
vote por la función de actualización del conjunto de HttpParams condicional: github.com/angular/angular/issues/26021
Serge

Respuestas:

94

Antes de 5.0.0-beta.6

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

Desde 5.0.0-beta.6

Desde 5.0.0-beta.6 (2017-09-03) agregaron una nueva función (aceptar mapa de objetos para encabezados y parámetros de HttpClient)

En el futuro, el objeto se puede pasar directamente en lugar de 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})
}
Michalis
fuente
Estoy usando 5.2.6 y definitivamente no tomará un diccionario para la pieza params a menos que lo haga explícitamente any. ¿Es ese realmente el uso previsto?
Gargoyle
6
@Gargoyle: puede lanzar su objeto a cualquiera { params: <any>params }para evitar problemas con el compilador de ts
Kieran
2
Los parámetros de tipo cadena que son nulos devolverán "nulo" y no
admiten
Aquí hay un enlace al informe de error por la falta de compatibilidad con números, booleanos y fechas: github.com/angular/angular/issues/23856
EpicVoyage
83

HttpParams está destinado a ser inmutable. Los métodos sety appendno modifican la instancia existente. En su lugar, devuelven nuevas instancias, con los cambios aplicados.

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

Este enfoque funciona bien con el encadenamiento de métodos:

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

... aunque eso podría ser incómodo si necesita envolver cualquiera de ellos en condiciones.

Su ciclo funciona porque está tomando una referencia a la nueva instancia devuelta. El código que publicaste que no funciona, no funciona. Simplemente llama a set () pero no toma el resultado.

let httpParams = new HttpParams().set('aaa', '111'); // now it has aaa
httpParams.set('bbb', '222');                        // result has both but is discarded
Rico
fuente
1
Qué extraña la elección de un HttpParams inmutable, este fue el punto de bloqueo y debería ser la respuesta aceptada
Nicolas
45

En versiones más recientes de @angular/common/http(5.0 y posteriores, por lo que parece), puede usar la fromObjectclave de HttpParamsOptionspara pasar el objeto directamente:

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

Sin embargo, esto solo tiene un forEachbucle debajo del capó :

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]);
});
Jonrsharpe
fuente
¿Por qué dices fromObject? Puede ser cualquier objeto.
Christian Matthew
@ChristianMatthew el constructor de HttpParams no puede tomar "ningún objeto", toma HttpParamsOptions. Si quiere decir que puede pasar cualquier objeto como parámetros a una solicitud: sí, pero solo desde v5. De lo contrario, no estoy seguro de qué estás preguntando
jonrsharpe
la palabra fromObject << ¿qué es eso correctamente
Christian Matthew
@ChristianMatthew No sé lo que estás preguntando. ¿Quieres saber qué es esa propiedad? ¿Miraste los documentos de la API? ¿O el ejemplo de la respuesta?
jonrsharpe
1
@ChristianMatthew No sé lo que estás preguntando. ¿Qué quieres decir con "versión API"? ¿Versión angular ? Puede ver a qué compromiso me he vinculado en la respuesta y dije explícitamente que se introdujo en 5.0. También puede ir y verlo en master ahora mismo: github.com/angular/angular/blob/master/packages/common/http/src/… . No tengo claro qué problema tienes.
jonrsharpe
21

En cuanto a mí, encadenar setmétodos es la forma más limpia

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
Mike Kovetsky
fuente
3
HttpParams es inmutable como HttpHeaders, lo que significa que la segunda llamada .set () no funcionará en este caso. Debe configurarse de un solo golpe o va con el método .append () asignando la salida a una nueva variable como sugirió el OP en su actualización.
WPalombini
2
@WPalombini Acabo de probar el conjunto de encadenamiento de Mike. ¡y funciona! Creo que es bueno para las personas que solo tienen 2 parámetros. Es fácil de entender.
Angela P
19

Un par de alternativas fáciles

Sin usar HttpParamsobjetos

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

this.http.post(url, body);

Usando HttpParamsobjetos

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

this.http.post(url, body);
Debojyoti
fuente
Esta debería ser la respuesta aceptada. Simple y limpio.
Karsus
14

Otra opción para hacerlo es:

this.httpClient.get('path', {
    params: Object.entries(data).reduce(
    (params, [key, value]) => params.set(key, value), new HttpParams());
});
Leibale Eidelman
fuente
Puede agregar un polyfill ( stackoverflow.com/questions/42410029/… )
Leibale Eidelman
tendré que probar esta magia
Christian Matthew
9

Dado que la clase HTTP Params es inmutable, debe encadenar el método set:

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
Adnan Abdul Khaliq
fuente
7

Usando esto puedes evitar el bucle.

// 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());

Además, sugiero hacer que toHttpParams funcione en su servicio de uso común. Entonces puede llamar a la función para convertir el objeto a 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());
}

Actualizar:

Desde 5.0.0-beta.6 (2017-09-03) agregaron una nueva función ( aceptar mapa de objetos para encabezados y parámetros de HttpClient )

En el futuro, el objeto se puede pasar directamente en lugar de HttpParams.

Esta es la otra razón por la que si ha utilizado una función común como toHttpParams mencionada anteriormente, puede eliminarla fácilmente o hacer cambios si es necesario.

Lahar Shah
fuente
Hola, está funcionando bien, pero agregue un valor clave indefinido adicional como
parámetro
3

Por lo que puedo ver en la implementación en https://github.com/angular/angular/blob/master/packages/common/http/src/params.ts

Debe proporcionar valores por separado: no puede evitar su bucle.

También hay un constructor que toma una cadena como parámetro, pero tiene la forma param=value&param2=value2 por lo que no hay ningún acuerdo para ti (en ambos casos terminarás con el bucle de tu objeto).

Siempre puede informar un problema / solicitud de función a angular, lo que recomiendo encarecidamente: https://github.com/angular/angular/issues

PD: Recuerde la diferencia entre los métodos sety append;)

Maciej Treder
fuente
¿Cuáles son las diferencias?
Christian Matthew
2

Dado que @MaciejTreder confirmó que tenemos que hacer un bucle, aquí hay un contenedor que opcionalmente le permitirá agregar a un conjunto de parámetros predeterminados:

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 };
}

Puedes usarlo así:

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
James Irwin
fuente
2

Mi clase auxiliar (ts) para convertir cualquier objeto dto complejo (no solo "diccionario de cadenas") a 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
iliakolesnikov
fuente
Realmente no necesitas esto, el Angular params.tstiene su propia codificación. Verifique la implementación: github.com/angular/angular/blob/master/packages/common/http/src/…
Davideas
1

Solo quería agregar eso si desea agregar varios parámetros con el mismo nombre de clave, por ejemplo: www.test.com/home?id=1&id=2

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

Use append, si usa set, sobrescribirá el valor anterior con el mismo nombre de clave.

Luigi Córdoba Granera
fuente
0

Estas soluciones funcionan para mí,

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

Rotceh
fuente