web-dev-qa-db-fra.com

Comment obtenir un observable pour renvoyer un tableau d'éléments transformés (Rxjs)

J'ai un point final qui produit une liste de produits JSON. J'ai un type de classe personnalisé défini dans mon code pour les produits. J'essaie d'obtenir les données du noeud final et de transformer le tableau json de produits en un tableau de la classe Product.

Exemple d'API json (simplifié à partir de mes données réelles): _

{
    "products": [{
        "id": 1,
        "name": "Product 1",
        "materials": [{
            "id": 100,
            "desc": "wood"
        }, {
            "id": 101,
            "desc": "metal"
        }]
    }, {
        "id": 2,
        "name": "Product 2",
        "materials": [{
            "id": 100,
            "desc": "wood"
        }, {
            "id": 102,
            "desc": "brick"
        }]
    }]
}

_ {Mon code:} _

loadProducts(){
    this.fetchProducts().subscribe(data => {
            console.log("the data:", data);
        })
}


fetchProducts(): Observable<Product[]> {
    return this.http.get("http://example.com/mydata.json")
        .map((response) => {
            const products = (response.json()).products;
            console.log("Converting to json" + products.length);
            return products;
        })
        .map( data => {
            console.log("Working on data: ", data);
            return new Product(data.id, data.name, data.materials);
        });

Ce que j'attendrais de voir dans ma console est ..

"Converting to json 2"
"Working on data: " [object]
"Working on data: " [object]
"the data:" [object,object]

..mais ce que je vois c'est ..

"Converting to json 2"
"Working on data: " [object,object]
"the data:" [object,object]

Je pensais que la fonction .map serait lancée pour chaque élément envoyé. Je peux voir que lorsque j'ai appelé .map pour la première fois, il est exécuté une fois sur l'un des éléments (la réponse) dont il dispose - et je sais que deux produits sont renvoyés. Je m'attendrais à ce que la deuxième fonction de carte soit ensuite exécutée deux fois, une fois pour chaque produit. Au lieu de cela, il semble que cela s'appelle une fois passé dans la gamme de produits.

Pour rendre les choses plus compliquées, je souhaite également convertir la liste des matériaux en un type de classe Matériau que j'ai créé. Je sais que je pourrais faire tous de cela avec des boucles forEach, mais je veux le faire de la manière "Réagir".

5
captain_jim1

J'ai finalement trouvé la bonne combinaison d'opérations Observable pour donner ce que je cherchais.

fetchProducts(): Observable<Product[]> {
    return this.http.get("http://examples.com/myData")
        .map((response) => {
            return response.json().products;
        })
        .switchMap( productArray => {
            return Observable.from(productArray);
        })
        .map( (productData: any) => {
            return new Product(
                productData.id,
                productData.name,
                productData.materials
            );
        })
        .toArray();
}

J'ai mal compris le fonctionnement d'Observable.map en pensant qu'il serait exécuté sur chaque élément de mes données lorsqu'il serait exécuté sur chaque élément de données .. et je disposais d'un élément de données, un tableau. Merci à @jonrsharpe de m'aider là-bas.

En utilisant switchMap pour renvoyer un nouvel observable de mon tableau, j'ai été en mesure d'émettre chaque donnée de mon tableau séparément. Merci @ giora-guttsait pour cette aide.

Enfin, je devais combiner tous ces nouveaux éléments du flux sur un seul tableau. Observable.toArray () l'a fait pour moi.

14
captain_jim1

Dans votre cas, vous obtenez quelque chose comme ça:

this.http.get(...)
  .map(res => res.json().products)  // turns this into Observable<Products[]>
  .map(products => ... ) // here, products is already an array.

Si vous souhaitez traiter ce tableau de la même manière que dans React, c'est-à-dire que vous devez faire quelque chose pour chaque produit, vous pouvez faire quelque chose comme:

this.http.get(...)
  .map(res => res.json().products)
  .switchMap(products => Observable.from(products))

Ici, Observable.from(products) renvoie Observable<Product> et switchMap rend le retour chaîne Observable<Product> que vous aviez auparavant.

this.http.get(...)
  .map(res => res.json().products)
  .switchMap(products => Observable.from(products))
  .subscribe(product => console.log(product))

Imprimera chacun de ces produits.

Disclamer: Je n'ai pas vérifié ce code en l'exécutant, vous voudrez peut-être un autre opérateur que switchMap, mais c'est ce que je me souviens qui fonctionne

1
Giora Guttsait