Http.get (), map (), subscribe () i obserwowalny wzorzec w Angular2 - podstawowe zrozumienie

170

Teraz mam stronę początkową, na której mam trzy linki. Po kliknięciu ostatniego łącza „znajomi” inicjowany jest odpowiedni komponent znajomych. W tym miejscu chcę pobrać / pobrać listę moich znajomych wpisanych do pliku friends.json. Do tej pory wszystko działa dobrze. Ale nadal jestem nowicjuszem w usłudze HTTP angular2, używającej obserwojów, mapy, subskrypcji RxJs. Próbowałem to zrozumieć i przeczytałem kilka artykułów, ale dopóki nie zabiorę się do praktycznej pracy, nie zrozumiem poprawnie tych pojęć.

Tutaj już stworzyłem plnkr, który działa z wyjątkiem pracy związanej z 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.

       }
  }

Prosimy o właściwe prowadzenie i wyjaśnianie. Wiem, że będzie to bardzo korzystne dla wielu nowych programistów.

nyks
źródło

Odpowiedzi:

205

Oto, gdzie popełniłeś błąd:

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

powinno być:

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

lub

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

Popełniłeś dwa błędy:

1- Przypisałeś obserwowalne samo this.result. Kiedy faktycznie chciałeś przypisać listę znajomych do this.result. Prawidłowy sposób to:

  • subskrybujesz to, co obserwowalne. .subscribejest funkcją, która faktycznie wykonuje obserwowalne. Wymaga trzech następujących parametrów wywołania zwrotnego:

    .subscribe(success, failure, complete);

na przykład:

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

Zwykle pobierasz wyniki z wywołania zwrotnego sukcesu i przypisujesz je do swojej zmiennej. wywołanie zwrotne błędu jest oczywiste. pełne wywołanie zwrotne służy do określenia, czy otrzymałeś ostatnie wyniki bez żadnych błędów. Na Twoim plunkerze pełne wywołanie zwrotne będzie zawsze wywoływane po pomyślnym lub błędnym wywołaniu zwrotnym.

2- Drugim błędem, dzwoniłeś .json()na .map(res => res.json()), wtedy nazywa się je ponownie zwrotnego sukces obserwowalne. .map()jest transformatorem, który przekształci wynik do tego, co zwrócisz (w twoim przypadku .json()), zanim zostanie przekazany do wywołania zwrotnego sukcesu, powinieneś wywołać go raz na jednym z nich.

Abdulrahman Alsoghayer
źródło
2
tutaj idziesz twój plunker . Zmieniłem linie: 21, 23 na myfriends.ts
Abdulrahman Alsoghayer
1
Czego nie rozumiem, to po co w ogóle używać tutaj funkcji „map”? Moglibyśmy po prostu zadzwonić do .json na wynik. Więc jakie są korzyści z tego?
rubmz
5
Masz rację @rubmz. Mógłbyś to zrobić, jak wspomniałem w mojej odpowiedzi, ale jedną ogromną korzyścią jest oddzielenie logiki. Na przykład w swojej usłudze masz funkcję getFriends(){return http.get('friends.json').map(r => r.json());}. Teraz możesz dzwonić getFriends().subscribe(...)bez konieczności dzwonienia za .json()każdym razem.
Abdulrahman Alsoghayer
2
Tak, jest to trochę mylące dla początkujących. Co robi ta tajemnicza mapa (), a co nie ... Ale w końcu też to dostałem :)
rubmz
1
@Abdulrahman, może zechcesz też rzucić okiem na to pytanie: stackoverflow.com/questions/40505691/ ...
nyluje
138

Koncepcje

Observables w skrócie dotyczy asynchronicznego przetwarzania i zdarzeń. W porównaniu do obietnic można to opisać jako obserwowalne = obietnice + zdarzenia.

Wspaniałe w przypadku obserwabli jest to, że są leniwe, można je anulować i można w nich zastosować pewne operatory (np. map, ...). Pozwala to na obsługę rzeczy asynchronicznych w bardzo elastyczny sposób.

Świetną próbką opisującą najlepiej moc obserwabli jest sposób połączenia wejścia filtra z odpowiednią przefiltrowaną listą. Kiedy użytkownik wprowadza znaki, lista jest odświeżana. Observables obsługują odpowiednie żądania AJAX i anulują poprzednie żądania w toku, jeśli inne jest wyzwalane przez nową wartość na wejściu. Oto odpowiedni kod:

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

( textValuejest sterowaniem skojarzonym z wejściem filtra).

Oto szerszy opis takiego przypadku użycia: Jak obserwować zmiany formy w Angular 2? .

Istnieją dwie świetne prezentacje na AngularConnect 2015 i EggHead:

Christoph Burgdorf napisał również kilka świetnych postów na ten temat:

W akcji

W rzeczywistości, jeśli chodzi o twój kod, pomieszałeś dwa podejścia ;-) Oto one:

  • Zarządzaj obserwowalnymi samodzielnie . W takim przypadku jesteś odpowiedzialny za wywołanie subscribemetody na obserowalnej i przypisanie wyniku do atrybutu komponentu. Następnie możesz użyć tego atrybutu w widoku do iteracji po kolekcji:

    @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();
       }
    }
    

    Zwroty z obu metod geti mapmetod są obserwowalne, a nie wynikiem (w taki sam sposób, jak w przypadku obietnic).

  • Pozwól zarządzać obserwowalnymi za pomocą szablonu Angular . Możesz również wykorzystać asyncpotok do niejawnego zarządzania obserwowalnymi. W takim przypadku nie ma potrzeby jawnego wywoływania subscribemetody.

    @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());
       }
    }
    

Możesz zauważyć, że obserwable są leniwe. Tak więc odpowiednie żądanie HTTP zostanie wywołane tylko raz, gdy odbiornik zostanie do niego dołączony przy użyciu subscribemetody.

Możesz również zauważyć, że mapmetoda jest używana do wyodrębnienia zawartości JSON z odpowiedzi i wykorzystania jej następnie w obserwowalnym przetwarzaniu.

Mam nadzieję, że to ci pomoże, Thierry

Thierry Templier
źródło
Dzięki za wszelkie referencje. Ale czy możesz mi pomóc z moją paczką?
nyks
Zaktualizowałem moją odpowiedź o więcej szczegółów dotyczących twojego kodu. Mam nadzieję, że ci to pomoże ;-)
Thierry Templier
przepraszam, że nie mogłem przyjąć twojej odpowiedzi. Jest bardziej przejrzysta, ale zaakceptowana i doceniona odpowiedź pomogła mi wystarczająco zrozumieć moje pytanie. Ale miejmy nadzieję, że otrzymasz dobre wyniki za jasną odpowiedź, ponieważ masz bardziej szczegółowe wyjaśnienie. Zaakceptowana odpowiedź również za dobre podstawowe zaniżenie.
micronyks
2
Thierry Templier to doskonała odpowiedź, ale jedno nie jest dla mnie jasne, pomyślałem, że http.get ('friends.json') .map (response => response.json ()) zwraca obserwowalne <Array <Object>>. Jeśli tak, to jak możesz to wysłać do tego wyniku? są to różne typy.
Stav Alfi
@StavAlfi pipessą również observables. obejrzyj ten film: youtube.com/watch?v=bVI5gGTEQ_U zasugerowany przez thierry, aby uzyskać więcej informacji.
candidJ
11
import { HttpClientModule } from '@angular/common/http';

Interfejs API HttpClient został wprowadzony w wersji 4.3.0. Jest to ewolucja istniejącego interfejsu API HTTP i ma własny pakiet @ angular / common / http. Jedną z najbardziej znaczących zmian jest to, że teraz obiekt odpowiedzi jest domyślnie JSON, więc nie ma już potrzeby analizowania go metodą map. Od razu możemy użyć jak poniżej

http.get('friends.json').subscribe(result => this.result =result);
rajesh kumar
źródło