Angular 4.3 - HttpClient ustawia parametry

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

Dlaczego to nie działa? Ustawia tylko „aaa”, a NIE „bbb”

Mam także obiekt {aaa: 111, bbb: 222} Jak mogę ustawić wszystkie wartości bez zapętlania?

UPDATE (to wydaje się działać, ale jak można uniknąć pętli?)

let httpParams = new HttpParams();
Object.keys(data).forEach(function (key) {
     httpParams = httpParams.append(key, data[key]);
});
Michalis
źródło
3
Zgadzam się z tobą, że to httpParams.set('bbb', '222');powinno działać. Spróbowałem tego jako pierwszy i byłem bardzo zdezorientowany. Ale zamień tę linię na httpParams = httpParams.set('bbb','222');działa. dla tych, którzy ustawiają tylko 2, odpowiedź łańcuchowa od innego użytkownika poniżej jest również miła.
Angela P
1
po prostu użyj przypisania (=) jak zasugerował @AngelaPan i nie musisz używać pętli. Przeczytaj także o zmienności i niezmienności.
Junaid
prosimy o głosowanie na funkcję warunkowej aktualizacji zestawu HttpParams
Serge

Odpowiedzi:

94

Przed 5.0.0-beta.6

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

Od wersji 5.0.0-beta.6

Od 5.0.0-beta.6 (2017-09-03) dodali nową funkcję (zaakceptuj mapę obiektów dla nagłówków i parametrów HttpClient)

Idąc dalej, obiekt może być przekazywany bezpośrednio zamiast 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
źródło
Używam 5.2.6 i na pewno nie będzie potrzebował słownika dla elementu params, chyba że wyraźnie to zrobię any. Czy to naprawdę przeznaczenie?
Gargoyle
6
@Gargoyle - możesz po prostu rzucić swój obiekt na dowolny, { params: <any>params }aby uniknąć problemów z kompilatorem TS
Kieran
2
Parametry typu string, które mają wartość null, zwrócą wartość „null” i nie
obsługują
Oto link do raportu błędu dotyczącego braku obsługi numerów, wartości logicznych i dat: github.com/angular/angular/issues/23856
EpicVoyage
84

HttpParams ma być niezmienny. Te seti appendmetody nie zmodyfikować istniejącą instancję. Zamiast tego zwracają nowe wystąpienia z zastosowanymi zmianami.

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

To podejście działa dobrze w przypadku łączenia metod:

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

... chociaż może to być niezręczne, jeśli musisz owinąć którykolwiek z nich w warunkach.

Twoja pętla działa, ponieważ pobierasz odniesienie do zwróconej nowej instancji. Kod, który opublikowałeś, który nie działa, nie działa. Po prostu wywołuje set (), ale nie przechwytuje wyniku.

let httpParams = new HttpParams().set('aaa', '111'); // now it has aaa
httpParams.set('bbb', '222');                        // result has both but is discarded
Bogaty
źródło
2
Jak dziwny był wybór niezmiennego HttpParams, to był punkt blokujący i powinien być akceptowaną odpowiedzią
Nicolas
48

@angular/common/httpWygląda na to, że w nowszych wersjach (5.0 i nowszych ) można użyć fromObjectklawisza do, HttpParamsOptionsaby przekazać obiekt bezpośrednio w:

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

To tylko tworzy forEachpętlę pod maską :

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
źródło
dlaczego mówisz fromObject? Może to być dowolny obiekt.
Christian Matthew
@ChristianMatthew Konstruktor HttpParams nie może przyjąć „żadnego obiektu”, pobiera HttpParamsOptions. Jeśli masz na myśli, że możesz przekazać dowolny obiekt jako parametry do żądania: tak, ale tylko z v5. W przeciwnym razie nie jestem pewien, o co pytasz.
jonrsharpe
słowo fromObject << co to właściwie jest
Christian Matthew
@ChristianMatthew Nie wiem, o co pytasz. Chcesz wiedzieć, co to za nieruchomość? Czy obejrzałeś dokumentację API? Albo przykład w odpowiedzi?
jonrsharpe
1
@ChristianMatthew Nie wiem, o co pytasz. Co masz na myśli przez „wersję API”. Wersja kątowa ? W odpowiedzi możesz zobaczyć, do którego zatwierdzenia utworzyłem łącze i powiedziałem wyraźnie, że zostało wprowadzone w wersji 5.0. Możesz także przejść i zobaczyć to teraz w master: github.com/angular/angular/blob/master/packages/common/http/src/… . Nie jest dla mnie jasne, jaki masz problem .
jonrsharpe
21

Jeśli chodzi o mnie, setmetody łańcuchowe to najczystsza droga

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
Mike Kovetsky
źródło
3
HttpParams jest niezmienna jak HttpHeaders, co oznacza, że ​​drugie wywołanie .set () nie zadziała w tym przypadku. Musi być ustawiony w jednym trafieniu lub iść z metodą .append () przypisując wynik do nowej zmiennej, zgodnie z sugestią OP podczas jego aktualizacji.
WPalombini
2
@WPalombini Właśnie wypróbowałem zestaw łańcuchowy Mike'a. i działa! Myślę, że to fajne dla ludzi, którzy mają tylko 2 parametry. Łatwo to zrozumieć.
Angela P
20

Kilka łatwych alternatyw

Bez używania HttpParamsobiektów

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

this.http.post(url, body);

Korzystanie z HttpParamsobiektów

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

this.http.post(url, body);
Debojyoti
źródło
To powinna być akceptowana odpowiedź. Proste i czyste.
Karsus
14

Inną opcją jest:

this.httpClient.get('path', {
    params: Object.entries(data).reduce(
    (params, [key, value]) => params.set(key, value), new HttpParams());
});
Leibale Eidelman
źródło
Możesz dodać wypełnienie polyfill ( stackoverflow.com/questions/42410029/… )
Leibale Eidelman
będę musiał spróbować tej magii
Christian Matthew
9

Ponieważ klasa HTTP Params jest niezmienna, musisz połączyć metodę set:

const params = new HttpParams()
.set('aaa', '111')
.set('bbb', "222");
Adnan Abdul Khaliq
źródło
7

Dzięki temu możesz uniknąć pętli.

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

Ponadto sugeruję włączenie funkcji toHttpParams w powszechnie używanej usłudze. Możesz więc wywołać funkcję, aby przekonwertować obiekt na 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());
}

Aktualizacja:

Od 5.0.0-beta.6 (2017-09-03) dodali nową funkcję ( zaakceptuj mapę obiektów dla nagłówków i parametrów HttpClient )

Idąc dalej, obiekt może być przekazywany bezpośrednio zamiast HttpParams.

To jest drugi powód, jeśli użyłeś jednej wspólnej funkcji, takiej jak wspomniana powyżej toHttpParams , możesz ją łatwo usunąć lub wprowadzić zmiany, jeśli to konieczne.

Lahar Shah
źródło
Cześć jego pracy grzywny, ale Dołącz dodatkowo zdefiniowana jako wartość klucza param
Abhijit Jagtap
3

O ile widzę z wdrożenia na https://github.com/angular/angular/blob/master/packages/common/http/src/params.ts

Musisz podać wartości osobno - nie możesz uniknąć pętli.

Istnieje również konstruktor, który przyjmuje łańcuch jako parametr, ale ma on postać param=value&param2=value2 więc nie ma dla Ciebie żadnej umowy (w obu przypadkach zakończysz zapętleniem obiektu).

Zawsze możesz zgłosić problem / prośbę o funkcję do angular, co zdecydowanie radzę: https://github.com/angular/angular/issues

PS: Pamiętaj o różnicy między metodami seti append;)

Maciej Treder
źródło
jakie są różnice?
Christian Matthew
2

Ponieważ @MaciejTreder potwierdził, że musimy zapętlić, oto opakowanie, które opcjonalnie pozwoli Ci dodać do zestawu domyślnych parametrów:

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

Możesz go używać w ten sposób:

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
źródło
2

Moja klasa pomocnicza (ts) do konwersji dowolnego złożonego obiektu dto (nie tylko „słownika ciągów”) do 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
źródło
Naprawdę tego nie potrzebujesz, Angular params.tsma własne kodowanie. Sprawdź implementację: github.com/angular/angular/blob/master/packages/common/http/src/…
Davideas
1

Chciałem tylko dodać, że jeśli chcesz dodać kilka parametrów o tej samej nazwie klucza, na przykład: www.test.com/home?id=1&id=2

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

Użyj dołączania, jeśli używasz set, nadpisze poprzednią wartość o tej samej nazwie klucza.

Luigi Cordoba Granera
źródło
0

To rozwiązanie działa dla mnie,

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

Rotceh
źródło