Angular之Http+RxJS之WebSocket(网络必备)

Get data from a server

  1. 在本教程中,您将在Angular的HttpClient的帮助下添加以下数据持久性特性。
  2. HeroService通过HTTP请求获取英雄数据。用户可以通过HTTP添加、编辑和删除英雄并保存这些更改。
  3. 用户可以通过名字搜索英雄。
一、 启用HTTP服务
  1. HttpClient是Angular用来通过HTTP与远程服务器通信的机制。

  2. 通过两个步骤使应用程序中的所有地方都可以使用HttpClient。首先,通过导入将其添加到根AppModule:

 ```
 <-src/app/app.module.ts (HttpClientModule import)->
 import { HttpClientModule }    from '@angular/common/http';
 ```
  1. *:*接下来,仍然在AppModule中,将HttpClient添加到导入数组中:

    <-src/app/app.module.ts (imports array excerpt)->
    @NgModule({
      imports: [
        HttpClientModule,
      ],
    })
    
二、模拟数据服务器
  1. 本教程示例使用内存中的Web API模块模拟与远程数据服务器的通信。

  2. 安装模块后,应用程序将向HttpClient发出请求并接收响应,而不知道内存中的Web API正在拦截这些请求,将它们应用到内存中的数据存储中,并返回模拟的响应。

  3. 通过使用内存中的Web API,您不必设置服务器来了解HttpClient。

    重要提示:在Angular中,内存中的Web API模块与HTTP无关。
    如果您只是阅读本教程以了解HttpClient,那么您可以跳过这一步。如果您正在编写本教程中的代码,请留在这里并立即添加内存中的Web API。
    
  4. 使用以下命令从npm安装内存中的Web API包:

    npm install angular-in-memory-web-api --save
    
  5. 在AppModule中,导入HttpClientInMemoryWebApiModule和稍后将创建的InMemoryDataService类。

    <-src/app/app.module.ts (In-memory Web API imports)->
    import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
    import { InMemoryDataService }  from './in-memory-data.service';
    
  6. 在HttpClientModule之后,将HttpClientInMemoryWebApiModule添加到AppModule import数组中,并使用InMemoryDataService配置它。

    <-src/app/app.module.ts (imports array excerpt)->
    HttpClientModule,
    
    // HttpClientInMemoryWebApiModule模块拦截HTTP请求
    // 返回模拟的响应数据
    // 在真正的服务器准备接收请求时删除它
    HttpClientInMemoryWebApiModule.forRoot(
      InMemoryDataService, { dataEncapsulation: false }
    )
    
  7. forRoot()配置方法接受一个InMemoryDataService类,该类启动内存中的数据库。

  8. 生成类src/app/in-memory-data.service。ts使用以下命令:

  ```
  ng generate service InMemoryData
  ```
  1. 用in-memory-data.service.ts的内容代替默认内容:

    <-src/app/in-memory-data.service.ts->
    import { InMemoryDbService } from 'angular-in-memory-web-api';
    import { Hero } from './hero';
    import { Injectable } from '@angular/core';
    
    @Injectable({
      providedIn: 'root',
    })
    export class InMemoryDataService implements InMemoryDbService {
      createDb() {
        const heroes = [
          { id: 11, name: 'Dr Nice' },
          { id: 12, name: 'Narco' },
          { id: 13, name: 'Bombasto' },
          { id: 14, name: 'Celeritas' },
          { id: 15, name: 'Magneta' },
          { id: 16, name: 'RubberMan' },
          { id: 17, name: 'Dynama' },
          { id: 18, name: 'Dr IQ' },
          { id: 19, name: 'Magma' },
          { id: 20, name: 'Tornado' }
        ];
        return {heroes};
      }
    
      // 重写genId方法以确保英雄始终具有id。
      // 如果heroes数组是空的,
      // 下面的方法返回初始数字(11)。
      // 如果heroes数组不是空的,则下面的方法返回最高的值
      // 英雄id + 1。
      genId(heroes: Hero[]): number {
        return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
      }
    }
    

    in-memory-data.service.ts文件代替了 mock-heroes.ts ,现在可以安全删除了。

    当服务器准备好时,您将分离内存中的Web API,应用程序的请求将经过服务器。

三、Heroes and HTTP
  1. 在HeroService中,导入HttpClient和HttpHeaders:

    <-src/app/hero.service.ts ->
    import { HttpClient, HttpHeaders } from '@angular/common/http';
    
  2. 仍然在HeroService中,将HttpClient注入到名为http的私有属性的构造函数中。

    <-src/app/hero.service.ts->
    constructor(
      private http: HttpClient,
      private messageService: MessageService) { }
    
  3. 注意,您一直在注入MessageService,但是由于您会频繁地调用它,所以将它封装在一个私有log()方法中:

    <-src/app/hero.service.ts->
    /** Log a HeroService message with the MessageService */
    private log(message: string) {
      this.messageService.add(`HeroService: ${message}`);
    }
    
  4. 使用服务器上英雄资源的地址来定义表单的heroesUrl:base/:collectionName。这里base是发出请求的资源,而collectionName是 in-memory-data-service.ts 中的heroes数据对象。

    <-src/app/hero.service.ts->
    private heroesUrl = 'api/heroes';  // URL to web api
    

四、Get heroes with HttpClient

  1. 当前的HeroService.getHeroes()使用()函数的RxJS来返回一个模拟英雄数组,作为一个可观察到的<Hero[]>。

    <-src/app/hero.service.ts (getHeroes with RxJs 'of()')->
    getHeroes(): Observable<Hero[]> {
      return of(HEROES);
    }
    
  2. 转换该方法使用HttpClient如下:

    <-src/app/hero.service.ts->
    /** GET heroes from the server */
    getHeroes (): Observable<Hero[]> {
      return this.http.get<Hero[]>(this.heroesUrl)
    }
    
    1. 刷新浏览器。应该可以成功地从模拟服务器加载英雄数据。
    2. 您已经将of()交换为http.get(),应用程序在没有任何其他更改的情况下继续工作,因为两个函数都返回一个可观察到的<Hero[]>。

HttpClient methods return one value

所有HttpClient方法都返回一个可观察到的RxJS。
HTTP是一个请求/响应协议。你发出一个请求,它会返回一个响应。
一般来说,一个被观察对象可以在一段时间内返回多个值。来自HttpClient的一个可观察对象总是发出一个单独的值,然后完成,不再发出。
这个特定的HttpClient.get()调用返回一个可观察的<Hero[]>;也就是说,“一个可见的英雄数组”。实际上,它只会返回一个英雄数组。

HttpClient.get() returns response data

  1. 默认情况下,HttpClient.get()以无类型JSON对象的形式返回响应主体。应用可选的类型说明符<Hero[]>,增加了TypeScript功能,减少了编译时的错误。

  2. 服务器的数据API决定JSON数据的形状。英雄指南数据API以数组的形式返回英雄数据。

```
其他api可能会将您想要的数据隐藏在对象中。您可能需要使用RxJS map()操作符来处理可观察结果,从而挖掘出数据。
虽然这里没有讨论,但是示例源代码中包含了getHeroNo404()方法中的map()示例。
```

Error handling

  1. 事情会出错,特别是当您从远程服务器获取数据时。getheroes()方法应该能捕获错误并执行适当的操作。

  2. 为了捕获错误,您可以通过RxJS的catchError()操作符从http.get()“管道”传输观察到的结果。

  3. 从rxjs/操作符以及稍后需要的其他操作符中导入catch错误符号。

 ```
 <-src/app/hero.service.ts->
 import { catchError, map, tap } from 'rxjs/operators';
 ```
  1. 现在使用pipe()方法扩展observable结果,并给它一个catchError()操作符。

    <-src/app/hero.service.ts->
    getHeroes (): Observable<Hero[]> {
      return this.http.get<Hero[]>(this.heroesUrl)
        .pipe(
          catchError(this.handleError<Hero[]>('getHeroes', []))//第一个参数是被监察的函数,第二个是返回的对象
        );
    }
    
  2. 操作符catchError()截获一个失败的观察目标。它向错误传递一个错误处理程序,该处理程序可以对错误做它想做的事情。

    1. 下面的handleError()方法报告错误,然后返回一个无害的结果,以便应用程序继续工作。
handleError
  1. 下面的handleError()将被许多HeroService方法共享,因此它被一般化以满足不同的需求。

  2. 它不是直接处理错误,而是返回一个错误处理函数来捕获它配置的错误,该函数使用失败操作的名称和一个安全的返回值。

 ```
 <-src/app/hero.service.ts->
 /**
  * 处理失败的Http操作。
  * 让应用程序继续。
  * @参数操作 - 失败操作的名称
  * @参数结果 - 可选值作为可观察结果返回
  */
 private handleError<T> (operation = 'operation', result?: T) {
   return (error: any): Observable<T> => {
 
     // TODO:将错误发送到远程日志记录基础设施
     console.error(error); // 改为登录控制台
 
     // TODO:更好地转换用户使用的错误
     this.log(`${operation} failed: ${error.message}`);
 
     // 通过返回一个空结果让应用程序继续运行。
     return of(result as T);
   };
 }
 ```
  1. 在向控制台报告错误之后,处理程序构造一个用户友好的消息并向应用程序返回一个安全值,以便应用程序可以继续工作。

    1. 因为每个服务方法都返回一种不同类型的可观察结果,所以handleError()接受一个类型参数,这样它就可以返回应用程序所期望的类型的安全值。

Tap into the Observable

  1. HeroService方法将进入可观察值流,并通过log()方法将消息发送到页面底部的消息区域。

  2. 他们将使用RxJS tap()操作符来完成这些操作,该操作符查看可观察值,对这些值执行一些操作,并将它们传递下去。tap()回调不会触及值本身。

  3. 下面是getHeroes()的最终版本,带有记录操作的tap()。

  ```
  <-src/app/hero.service.ts->
  /** GET heroes from the server */
  getHeroes (): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError<Hero[]>('getHeroes', []))
      );
  }
  ```

Get hero by id

  1. 大多数web api都支持以下形式的get by id请求:baseURL/:id。

  2. 这里,基本URL是在Heroes和HTTP部分(api/ Heroes)中定义的heroesURL, id是您想要检索的英雄的编号。例如,api /英雄/ 11。

  3. 使用以下方法更新HeroService getHero()方法以发出请求:

  ```
  <-src/app/hero.service.ts->
  /** GET hero by id. Will 404 if id not found */
  getHero(id: number): Observable<Hero> {
    const url = `${this.heroesUrl}/${id}`;
    return this.http.get<Hero>(url).pipe(
      tap(_ => this.log(`fetched hero id=${id}`)),
      catchError(this.handleError<Hero>(`getHero id=${id}`))
    );
  }
  ```
  1. 与getHeroes()有三个显著的区别:

    getHero()使用所需的英雄id构造一个请求URL。
    服务器应该使用单个英雄响应,而不是一组英雄。
    getHero()返回一个可观察到的<Hero>(“一个可观察到的Hero对象”),而不是一个可观察到的Hero数组。
    
Update heroes
  1. 在英雄详情视图中编辑一个英雄的名字。当您键入时,英雄名称将更新页面顶部的标题。但是当你点击“返回”按钮时,改变就会丢失。

  2. 如果希望更改保持不变,则必须将它们写回服务器 。

  3. 在hero detail模板的末尾,添加一个带有click事件绑定的save按钮,该按钮调用一个名为save()的新组件方法。

  ```
  <-src/app/hero-detail/hero-detail.component.html (save)->
  <button (click)="save()">save</button>
  ```
  1. 在HeroDetail组件类中,添加以下save()方法,该方法使用hero服务updateHero()方法持久保存英雄的名字更改,然后导航回之前的视图。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值