Angular2中的http.get()、map()、subscribe()和observable模式 - 基本理解

179

现在,我有一个初始页面,在那里我有三个链接。一旦您单击最后的“friends”链接,适当的朋友组件就会被初始化。在那里,我想获取存储在friends.json文件中的我的朋友列表。

到目前为止,一切都运行良好。但我对于使用RxJs的observable、map、subscribe概念的angular2的HTTP服务仍然是一个新手。我尝试理解它并阅读了一些文章,但直到我实际操作才能正确理解这些概念。

在这里,我已经制作了一个工作正常(除了HTTP相关的工作)的plnkr。

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.

       }
  }
请正确地指导和解释。我知道这对许多新开发人员会非常有益。
3个回答

208

你犯的错误在这里:

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

应该是:

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

你犯了两个错误:

1- 你将可观察对象本身分配给了 this.result。实际上,你想要将朋友列表分配给 this.result。正确的做法是:

  • 你需要订阅这个可观察对象。 .subscribe 是实际执行可观察对象的函数。它接受三个回调参数,如下所示:

    .subscribe(success, failure, complete);

例如:

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

通常,您将成功回调中的结果分配给变量。错误回调是不言自明的。 完整回调用于确定您是否已收到最后的结果且没有任何错误。 在您的plunker中,完整回调将在成功或错误回调之后始终被调用。

2- 第二个错误是,在.map(res => res.json())上调用了.json(),然后在observable的成功回调上又调用了一次。 .map()是一个转换器,会将结果转换为您返回的任何内容(在您的情况下是.json()),然后再传递给成功回调。 您应该在其中一个上只调用一次。


2
请查看您的Plunker链接:your plunker。我已经在myfriends.ts文件中更改了第21行和第23行。 - Abdulrahman Alsoghayer
1
我不理解的是为什么这里要使用“map”函数?我们可以直接在结果上调用“.json”。那么这样做的好处是什么? - rubmz
5
你是正确的,@rubmz。你可以按照我在答案中提到的方式做。但是,一个巨大的好处就是分离逻辑。例如,在你的服务中,有一个函数getFriends(){return http.get('friends.json').map(r => r.json());}。现在,你可以调用getFriends().subscribe(...)而不需要每次都调用.json() - Abdulrahman Alsoghayer
3
没错,对于新手来说这可能有点令人困惑。那个神秘的map()函数到底是做什么的,又不是做什么的……但最终我也弄懂了 :) - rubmz
1
@BrijeshMavani result 只是一個參數名稱。您可以使用任何喜歡的名稱替換它。您可以將其寫成 .subscribe(a=>this.result= a.json)。這完全相同。 - Abdulrahman Alsoghayer
显示剩余8条评论

139

概念

简单来说,Observables处理异步处理和事件。与Promises相比,可将其描述为:observables= promises + events。

Observables的优势在于它们是惰性的,可以取消,并且可以对它们应用一些操作符(如map,...)。这允许以非常灵活的方式处理异步事物。

展示Observables强大之处的一个很好的例子是将筛选器输入与相应的筛选列表连接起来的方式。当用户输入字符时,列表会刷新。Observables处理相应的AJAX请求,并且如果输入中的新值触发了另一个请求,则会取消先前正在进行的请求。以下是相应的代码:

this.textValue.valueChanges
    .debounceTime(500)
    .switchMap(data => this.httpService.getListValues(data))
    .subscribe(data => console.log('new list values', data));
(textValue 是与筛选输入控件相关联的控件)。
以下是此用例的更广泛描述:如何在Angular中监视表单更改
AngularConnect 2015和EggHead有两个很棒的演示:
- Observables vs promises - https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises - Creating-an-observable - https://egghead.io/lessons/rxjs-creating-an-observable - RxJS深入https://www.youtube.com/watch?v=KOOT7BArVHQ - Angular 2数据流 - https://www.youtube.com/watch?v=bVI5gGTEQ_U Christoph Burgdorf也在此主题上撰写了一些很棒的博客文章:
- http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html - http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html 实际应用 实际上,关于你的代码,你混合了两种方法;-) 以下是它们:
- 通过自己管理可观察对象。在这种情况下,您负责在可观察对象上调用subscribe方法,并将结果分配给组件的属性。然后,您可以在视图中使用此属性迭代集合:
  @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();
     }
  }

与 Promise 一样,getmap 方法的返回值是 Observable 而不是结果。

  • 通过 Angular 模板来管理 Observable 。你还可以使用 async 管道来隐式管理 Observable 。在这种情况下,无需显式调用 subscribe 方法。

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

    您可以注意到,可观察对象是惰性的。因此,只有在使用subscribe方法附加了一个侦听器后,相应的HTTP请求才会被调用一次。

    您还可以注意到map方法用于从响应中提取JSON内容,并将其在可观察处理中使用。


    谢谢您提供的所有参考资料。但是,您能帮我解决我的代码问题吗? - nyks
    我更新了我的回答,添加了关于你代码的更多细节。希望能对你有所帮助 ;-) - Thierry Templier
    很抱歉我不能接受你的答案。虽然它更清晰,但被接受和赞赏的答案已经帮助我足够理解我的问题。但愿你的清晰回答能得到好的反响,因为你有更详细的解释。被接受的答案也是为了良好的基础理解。 - micronyks
    2
    Thierry Templier 这是一个很好的回答,但有一件事对我不清楚,我认为 http.get('friends.json').map(response => response.json()) 返回 observable<Array<Object>>。如果是这样,那么你是如何将它发送到 this.result 的?它们是不同的类型。 - Stav Alfi
    @StavAlfi pipes 也是一种 observables。建议查看由Thierry推荐的此视频:https://www.youtube.com/watch?v=bVI5gGTEQ_U,以获取更多信息。 - candidJ
    @StavAlfi 实际上,async 管道会在内部处理对可观察对象的订阅,因此您不需要自己处理它... 您是正确的,您的代码返回 Observable<Array<Object>> - Thierry Templier

    11
    import { HttpClientModule } from '@angular/common/http';
    

    HttpClient API在4.3.0版本中引入。它是现有HTTP API的演进,并拥有自己的包@angular/common/http。 最显着的变化之一是,现在响应对象默认为JSON,因此不再需要使用map方法解析它。我们可以直接像下面这样使用

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

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接