Angular2 http.get (), map (), subscribe () et motif observable - compréhension de base

170

Maintenant, j'ai une page initiale où j'ai trois liens. Une fois que vous cliquez sur le dernier lien «amis», le composant amis approprié est lancé. Là-dedans, je veux récupérer / obtenir la liste de mes amis dans le fichier friends.json. Jusqu'à présent, tout fonctionne bien. Mais je suis toujours un novice pour le service HTTP d'angular2 utilisant le concept d'observables, de carte et d'abonnement de RxJs. J'ai essayé de le comprendre et lu quelques articles, mais tant que je n'aurai pas commencé un travail pratique, je ne comprendrai pas ces concepts correctement.

Ici, j'ai déjà fait plnkr qui fonctionne sauf le travail lié à HTTP.

Plnkr

myfriends.ts

 import {Component,View,CORE_DIRECTIVES} from 'angular2/core';
 import {Http, Response,HTTP_PROVIDERS} from 'angular2/http';
 import 'rxjs/Rx';
 @Component({
    template: `
    <h1>My Friends</h1>
    <ul>
      <li *ngFor="#frnd of result">
          {{frnd.name}} is {{frnd.age}} years old.
      </li>
    </ul>
    `,
    directive:[CORE_DIRECTIVES]
  })

  export class FriendsList{

      result:Array<Object>; 
      constructor(http: Http) { 
        console.log("Friends are being called");

       // below code is new for me. So please show me correct way how to do it and please explain about .map and .subscribe functions and observable pattern.

        this.result = http.get('friends.json')
                      .map(response => response.json())
                      .subscribe(result => this.result =result.json());

        //Note : I want to fetch data into result object and display it through ngFor.

       }
  }

Veuillez guider et expliquer correctement. Je sais que ce sera très bénéfique pour de nombreux nouveaux développeurs.

nyks
la source

Réponses:

205

Voici où vous vous êtes trompé:

this.result = http.get('friends.json')
                  .map(response => response.json())
                  .subscribe(result => this.result =result.json());

ça devrait être:

http.get('friends.json')
                  .map(response => response.json())
                  .subscribe(result => this.result =result);

ou

http.get('friends.json')
                  .subscribe(result => this.result =result.json());

Vous avez commis deux erreurs:

1- Vous avez attribué l'observable lui-même this.result. Lorsque vous vouliez réellement attribuer la liste d'amis à this.result. La bonne façon de procéder est:

  • vous souscrivez à l'observable. .subscribeest la fonction qui exécute réellement l'observable. Il faut trois paramètres de rappel comme suit:

    .subscribe(success, failure, complete);

par exemple:

.subscribe(
    function(response) { console.log("Success Response" + response)},
    function(error) { console.log("Error happened" + error)},
    function() { console.log("the subscription is completed")}
);

Habituellement, vous prenez les résultats du rappel de réussite et les affectez à votre variable. le rappel d'erreur est explicite. le rappel complet est utilisé pour déterminer que vous avez reçu les derniers résultats sans aucune erreur. Sur votre plunker, le rappel complet sera toujours appelé après le rappel de réussite ou d'erreur.

2- La deuxième erreur, vous a appelé .json()à .map(res => res.json()), puis vous l' avez appelé à nouveau sur le rappel de la réussite de l'observable. .map()est un transformateur qui transformera le résultat en ce que vous retournez (dans votre cas .json()) avant qu'il ne soit passé au rappel de succès, vous devez l'appeler une fois sur l'un ou l'autre.

Abdulrahman Alsoghayer
la source
2
ici vous allez votre plunker . J'ai changé de ligne: 21, 23 sur myfriends.ts
Abdulrahman Alsoghayer
1
Ce que je n'ai pas compris, c'est pourquoi utiliser la fonction "carte" ici? Nous pourrions simplement appeler le .json sur le résultat. Alors quel est l'avantage de le faire?
rubmz
5
Vous avez raison @rubmz. Vous pourriez faire cela comme je l'ai mentionné dans ma réponse, mais un avantage énorme est de séparer la logique. Par exemple, dans votre service, vous avez une fonction getFriends(){return http.get('friends.json').map(r => r.json());}. Désormais, vous pouvez appeler getFriends().subscribe(...)sans avoir à appeler à .json()chaque fois.
Abdulrahman Alsoghayer
2
Oui, c'est juste un peu déroutant pour les débutants. Ce que fait cette mystérieuse carte () et ce qui ne l'est pas ... Mais finalement je l'ai aussi compris :)
rubmz
1
@Abdulrahman, vous serez peut-être intéressé à jeter un œil à cette question aussi: stackoverflow.com/questions/40505691/…
nyluje
138

Concepts

Les observables en bref abordent le traitement et les événements asynchrones. Comparé aux promesses, cela pourrait être décrit comme observables = promesses + événements.

Ce qui est génial avec les observables, c'est qu'ils sont paresseux, ils peuvent être annulés et vous pouvez y appliquer des opérateurs (comme map, ...). Cela permet de gérer les choses asynchrones de manière très flexible.

Un excellent exemple décrivant au mieux la puissance des observables est la manière de connecter une entrée de filtre à une liste filtrée correspondante. Lorsque l'utilisateur entre des caractères, la liste est actualisée. Les observables gèrent les requêtes AJAX correspondantes et annulent les requêtes en cours précédentes si une autre est déclenchée par une nouvelle valeur dans l'entrée. Voici le code correspondant:

this.textValue.valueChanges
    .debounceTime(500)
    .switchMap(data => this.httpService.getListValues(data))
    .subscribe(data => console.log('new list values', data));

( textValueest le contrôle associé à l'entrée du filtre).

Voici une description plus large d'un tel cas d'utilisation: Comment surveiller les changements de formulaire dans Angular 2? .

Il y a deux excellentes présentations à AngularConnect 2015 et EggHead:

Christoph Burgdorf a également écrit quelques bons articles de blog sur le sujet:

En action

En fait concernant votre code, vous avez mélangé deux approches ;-) Les voici:

  • Gérez vous-même l'observable . Dans ce cas, vous êtes responsable d'appeler la subscribeméthode sur l'observable et d'affecter le résultat dans un attribut du composant. Vous pouvez ensuite utiliser cet attribut dans la vue pour itérer sur la collection:

    @Component({
      template: `
        <h1>My Friends</h1>
        <ul>
          <li *ngFor="#frnd of result">
            {{frnd.name}} is {{frnd.age}} years old.
          </li>
        </ul>
      `,
      directive:[CORE_DIRECTIVES]
    })
    export class FriendsList implement OnInit, OnDestroy {
      result:Array<Object>; 
    
      constructor(http: Http) {
      }
    
      ngOnInit() {
        this.friendsObservable = http.get('friends.json')
                      .map(response => response.json())
                      .subscribe(result => this.result = result);
       }
    
       ngOnDestroy() {
         this.friendsObservable.dispose();
       }
    }
    

    Les retours des deux méthodes getet mapsont l'observable et non le résultat (de la même manière qu'avec les promesses).

  • Laissez gérer l'observable par le modèle angulaire . Vous pouvez également tirer parti du asynccanal pour gérer implicitement l'observable. Dans ce cas, il n'est pas nécessaire d'appeler explicitement la subscribeméthode.

    @Component({
      template: `
        <h1>My Friends</h1>
        <ul>
          <li *ngFor="#frnd of (result | async)">
            {{frnd.name}} is {{frnd.age}} years old.
          </li>
        </ul>
      `,
      directive:[CORE_DIRECTIVES]
    })
    export class FriendsList implement OnInit {
      result:Array<Object>; 
    
      constructor(http: Http) {
      }
    
      ngOnInit() {
        this.result = http.get('friends.json')
                      .map(response => response.json());
       }
    }
    

Vous pouvez remarquer que les observables sont paresseux. Ainsi, la requête HTTP correspondante ne sera appelée qu'une seule fois un écouteur avec attaché dessus en utilisant la subscribeméthode.

Vous pouvez également remarquer que la mapméthode est utilisée pour extraire le contenu JSON de la réponse et l'utiliser ensuite dans le traitement observable.

J'espère que cela vous aide, Thierry

Thierry Templier
la source
Merci pour toutes les références. Mais pouvez-vous m'aider avec mon plunk?
nyks
J'ai mis à jour ma réponse avec plus de détails concernant votre code. J'espère que cela vous aidera ;-)
Thierry Templier
désolé de ne pas pouvoir accepter votre réponse. C'est une réponse plus claire mais acceptée et appréciée qui m'a aidé à comprendre suffisamment ma question. Mais j'espère que vous obtiendrez de bons résultats pour votre réponse claire car vous avez des explications plus détaillées. Réponse acceptée aussi pour une bonne compréhension de base.
micronyks
2
Thierry Templier c'est une excellente réponse mais une chose n'est pas claire pour moi, j'ai pensé que http.get ('friends.json') .map (response => response.json ()) renvoie observable <Array <Object>>. Si oui, comment se fait-il que vous l'envoyiez à ce résultat? ce sont des types différents.
Stav Alfi le
@StavAlfi pipesest également un observables. regardez cette vidéo: youtube.com/watch?v=bVI5gGTEQ_U suggéré par thierry pour plus d'informations.
candidJ
11
import { HttpClientModule } from '@angular/common/http';

L'API HttpClient a été introduite dans la version 4.3.0. Il s'agit d'une évolution de l'API HTTP existante et possède son propre package @ angular / common / http. L'un des changements les plus notables est que maintenant l'objet de réponse est un JSON par défaut, il n'est donc plus nécessaire de l'analyser avec la méthode map.

http.get('friends.json').subscribe(result => this.result =result);
Rajesh Kumar
la source