Angular 4.3 - parâmetros definidos do HttpClient

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

Por que isso não funciona? Ele apenas define o 'aaa' e NÃO o 'bbb'

Além disso, tenho um objeto {aaa: 111, bbb: 222} Como posso definir todos os valores sem loop?

ATUALIZAÇÃO (parece funcionar, mas como evitar o loop?)

let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
     httpParams = httpParams.append(key, data[key]);
});
Michalis
fonte
3
Eu concordo com você que httpParams.set('bbb', '222');deve funcionar. Eu tentei isso primeiro e fiquei muito confuso. Mas substitua essa linha por httpParams = httpParams.set('bbb','222');obras. para aqueles que estão configurando apenas 2, o encadeamento de resposta de outro usuário abaixo também é bom.
Angela P
1
apenas use a atribuição (=) como @AngelaPan sugeriu e você não precisa usar o loop. Leia também sobre mutável e imutável.
Junaid de
vote no recurso de atualização do conjunto HttpParams condicional: github.com/angular/angular/issues/26021
Serge,

Respostas:

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 (03-09-2017), eles adicionaram um novo recurso (aceitar o mapa de objetos para cabeçalhos e parâmetros HttpClient)

Daqui para frente, o objeto pode ser passado diretamente em vez 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
fonte
Estou usando o 5.2.6 e definitivamente não será necessário um dicionário para a parte dos parâmetros, a menos que eu explicitamente o faça any. Esse é realmente o uso pretendido?
Gargoyle
6
@Gargoyle - você pode apenas lançar seu objeto para qualquer um { params: <any>params }para evitar problemas de compilador ts
Kieran
2
Os parâmetros do tipo string que são nulos retornarão "nulo" e não suporta número, booleano e data
Omar AMEZOUG
Aqui está um link para o relatório de bug para a falta de suporte para número, booleano e data: github.com/angular/angular/issues/23856
EpicVoyage
84

HttpParams deve ser imutável. Os métodos sete appendnão modificam a instância existente. Em vez disso, eles retornam novas instâncias, com as alterações aplicadas.

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

Esta abordagem funciona bem com encadeamento de métodos:

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

... embora isso possa ser estranho se você precisar envolver qualquer um deles em condições.

Seu loop funciona porque você está capturando uma referência para a nova instância retornada. O código que você postou que não funciona, não funciona. Ele apenas chama set (), mas não pega o resultado.

let httpParams = new HttpParams().set('aaa', '111'); // now it has aaa
httpParams.set('bbb', '222');                        // result has both but is discarded
Rico
fonte
2
Que bizarra a escolha de um HttpParams imutável, esse foi o ponto de bloqueio e deve ser a resposta aceita
Nicolas
48

Em versões mais recentes do @angular/common/http(5.0 e superiores, pelo que parece), você pode usar a fromObjectchave de HttpParamsOptionspara passar o objeto diretamente em:

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

Isso apenas faz um forEachloop sob o 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
fonte
por que você está dizendo fromObject? Pode ser qualquer objeto.
Christian Matthew
@ChristianMatthew o construtor de HttpParams não pode pegar "nenhum objeto", ele pega HttpParamsOptions. Se você quer dizer, pode passar qualquer objeto como parâmetros para uma solicitação: sim, mas apenas a partir da v5. Caso contrário, não tenho certeza do que você está perguntando.
Jonrsharpe
a palavra do Objeto << o que é isso corretamente
Christian Mateus
@ChristianMatthew Não sei o que você está perguntando. Você quer saber o que é essa propriedade? Você olhou os documentos da API? Ou o exemplo na resposta?
Jonrsharpe
1
@ChristianMatthew Não sei o que você está perguntando. O que quer dizer "versão API". Versão angular ? Você pode ver a qual commit vinculei na resposta e disse explicitamente que foi introduzido no 5.0. Você também pode ir e ver no master agora: github.com/angular/angular/blob/master/packages/common/http/src/… . Não está claro para mim qual é o seu problema .
Jonrsharpe
21

Quanto a mim, os setmétodos de encadeamento são a maneira mais limpa

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
Mike Kovetsky
fonte
3
HttpParams é imutável como HttpHeaders, o que significa que a segunda chamada .set () não funcionará neste caso. Ele precisa ser definido em um hit ou vai com o método .append () atribuindo a saída a uma nova variável como o OP sugerido em sua atualização.
WPalombini
2
@WPalombini Acabei de experimentar o conjunto de encadeamento de Mike. e funciona! Eu acho legal pra gente que só tem 2 parâmetros. É fácil de entender.
Angela P
20

Algumas alternativas fáceis

Sem 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
fonte
Esta deve ser a resposta aceita. Simples e limpo.
Karsus
14

Outra opção para fazer isso é:

this.httpClient.get('path', {
    params: Object.entries(data).reduce(
    (params, [key, value]) => params.set(key, value), new HttpParams());
});
Leibale Eidelman
fonte
Você pode adicionar um polyfill ( stackoverflow.com/questions/42410029/… )
Leibale Eidelman
vou ter que tentar essa magia
Christian Matthew
9

Como a classe HTTP Params é imutável, você precisa encadear o método definido:

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

Usando isso, você pode evitar o 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());

Além disso, sugiro fazer a função toHttpParams em seu serviço comumente usado. Portanto, você pode chamar a função para converter o objeto em 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());
}

Atualizar:

Desde 5.0.0-beta.6 (03-09-2017), eles adicionaram um novo recurso ( aceitar mapa de objetos para cabeçalhos e parâmetros HttpClient )

Daqui para frente, o objeto pode ser passado diretamente em vez de HttpParams.

Este é o outro motivo, se você usou uma função comum como toHttpParams mencionada acima, você pode removê-la facilmente ou fazer alterações, se necessário.

Lahar Shah
fonte
Olá, está funcionando bem, mas acrescente um valor-chave extra indefinido como parâmetro
Abhijit Jagtap
3

Pelo que posso ver da implementação em https://github.com/angular/angular/blob/master/packages/common/http/src/params.ts

Você deve fornecer valores separadamente - Você não pode evitar seu loop.

Há também um construtor que recebe uma string como parâmetro, mas está na forma, param=value&param2=value2então não há acordo para você (em ambos os casos, você terminará com o loop de seu objeto).

Você sempre pode relatar um problema / solicitação de recurso para angular, o que eu recomendo fortemente: https://github.com/angular/angular/issues

PS: Lembre-se da diferença entre os métodos sete append;)

Maciej Treder
fonte
Quais são as diferenças?
Christian Matthew
2

Uma vez que @MaciejTreder confirmou que precisamos fazer um loop, aqui está um wrapper que, opcionalmente, permitirá que você adicione a um conjunto de parâmetros padrão:

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

Você pode usá-lo assim:

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
fonte
2

Minha classe auxiliar (ts) para converter qualquer objeto dto complexo (não apenas "dicionário de string") em 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
fonte
Você realmente não precisa disso, o Angular params.tstem sua própria codificação. Verifique a implementação: github.com/angular/angular/blob/master/packages/common/http/src/…
Davideas
1

Só queria adicionar isso se você quiser adicionar vários parâmetros com o mesmo nome de chave, por exemplo: www.test.com/home?id=1&id=2

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

Use append, se você usar set, ele sobrescreverá o valor anterior com o mesmo nome de chave.

Luigi Cordoba Granera
fonte
0

Essas soluções estão funcionando para mim,

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

Rotceh
fonte