尝试在我当前的 Angular 应用程序中使用异步管道而不是订阅

Posted

技术标签:

【中文标题】尝试在我当前的 Angular 应用程序中使用异步管道而不是订阅【英文标题】:Trying to use Async pipe instead of subscribe in my current Angular application 【发布时间】:2018-03-04 15:12:57 【问题描述】:

我正在开发一个 Angular(v4) 应用程序。与任何其他 Web 应用程序一样,此应用程序也有 HTTP 请求,对于每个请求,我都有一个订阅以获取数据并在 UI 上显示它们。我一直在阅读async 管道的使用以及为什么它比subscribe 更好。根据我的阅读,它肯定比普通的subscribe 方法有好处,我相信我应该在我的应用程序中使用它,但我不确定如何使用这个aync 管道而不是subscribe 来构建我的应用程序。让我发布我的应用程序的结构:

我有一个向后端发出Http 请求的服务层,类似于:

some-service.component.ts

@Injectable()
export class SomeService 
 constructor(private http: HttpClient) 

 getSomething() 
    return this.http.get(`$this.baseUrl/$'test'`);


上述服务为我的组件提供了一个 Observable,我通常会订阅它,如下所示:

一些组件.ts

    @Component(
    selector: 'some-app',
    templateUrl: './some.component.html',
    styleUrls: []
)
export class SomeComponent 
 constructor(private someService: SomeService) 
    
  getSomething() 
        this.someService
            .getSomething()
            .subscribe(
                // the first argument is a function which runs on success
                (data) => 
                  // DO SOME DATA TRANSFORMATION HERE
                                    ,
                // the second argument is a function which runs on error
                (err: HttpErrorResponse) => 
                    this.exceptionService.errorResponse(err);
                ,
                // the third argument is a function which runs on completion
                () => 
                      // MAKE SOME FUNCTION CALL
                                
            );
    

以上是我如何订阅一般HTTP 响应的一般结构。我想摆脱这个subscribe 并改用async。但是,我的问题是,我在收到数据时会做一些data transformation(如上所示)或有时I make some function call from thecompletionobserver(如上所示),并且通过这样的操作,我不确定我将如何这些操作一旦切换到async

我想,对于数据转换,我可以在我的服务中完成(虽然不确定),但我如何才能像现在这样进行函数调用。

任何意见将不胜感激。如果我的问题需要更多说明,请告诉我。谢谢!

【问题讨论】:

【参考方案1】:

async pipe 像这样应用于您的模板绑定

<div *ngFor="let a of yourData | async">
    <p>a.whatever</p>
</div>

如果您不知道 a 是您要调用每个数组的任何名称,yourData 是您要从中迭代的类中变量的名称。

我不一定会说这比使用subscribe()“更好”,因为它不是做同样事情的替代方法。事实上,async 管道可以帮助您的绑定处理订阅,因为当组件加载时,它可能会在数据到达之前调用变量的值,这会给您带来一堆 undefined 错误或在您的 @ 中看不到任何内容987654328@ 即使您在模板中看到它绑定。

您试图通过嗅探async 管道来消除哪些令人头疼的问题?听起来您还需要了解其他一些您不知道如何要求的东西,这是尝试发现它。

【讨论】:

感谢您的回复。使用异步,我试图查看是否所有订阅(用于 HTTP 请求)都可以用异步管道替换。而且我遇到了问题,因为我不一定了解异步是如何工作的。因此,首先,我认为 async 与 subscribe 类似,但语法和编写方式不同。另外,我试图发现如何使用订阅的所有三个回调,即下一个、错误和异步完成。我希望,我已经把自己说清楚了。【参考方案2】:

1) 您可以创建一个custom pipe 来进行数据转换 (docs)

2) 你可以使用catch() 方法来处理错误(docs)

【讨论】:

感谢您的回答。根据您的输入,data transformationerror handling 可以使用 async 处理。 subscribe 也有第三个 completion 观察者,一旦数据在我们的组件中可用,我们就可以在其中执行某些任务。当我使用async 时,我该如何做同样的事情?【参考方案3】:

1。接收数据后进行数据转换

当您使用 observables 时,您可以使用一个非常常见的运算符将传入的数据转换为其他东西:地图运算符 (documentation here)。 "Map 运算符将您选择的函数应用于源 Observable 发出的每个项目,并返回一个发出这些函数应用程序结果的 Observable。"

some-service.component.ts(修改)

// Don't forget this, or it will cause errors
import 'rxjs/add/operator/map';

@Injectable()
export class SomeService 
  constructor(private http: HttpClient) 

getSomething() 
  return this.http.get(`$this.baseUrl/$'test'`)
    .map(data => transformData(data));

一些组件.ts

@Component(
  selector: 'some-app',
  templateUrl: './some.component.html',
  styleUrls: []
)
export class SomeComponent 

  constructor(private someService: SomeService)  

  getSomething() 
    this.someService
      .getSomething()
        .subscribe(
          // the first argument is a function which runs on success
          (data) => 

            // DO SOMETHING WITH DATA TRANSFORMED HERE
                                ,
          // the second argument is a function which runs on error
          (err: HttpErrorResponse) => 

            this.exceptionService.errorResponse(err);

          ,
          // the third argument is a function which runs on completion
          () => 

            // MAKE SOME FUNCTION CALL

          
        );
  

请注意,您还可以在 some-component.ts 中再次使用 map 运算符(在导入之后)。您也可以决定只在 some-component.ts 中使用它一次。或者您可以将一个函数传递给 this.someService.getSomething(myFunction) 来转换数据。请参阅下面的服务实现。

// Don't forget this, or it will cause errors
import 'rxjs/add/operator/map';

@Injectable()
export class SomeService 
  constructor(private http: HttpClient) 

getSomething(myFunction) 
  return this.http.get(`$this.baseUrl/$'test'`)
    .map(data => myFunction(data));

2。使用异步管道代替订阅

要使用异步管道,请将 Observable 存储在属性中。

import  Component, OnInit  from '@angular/core'

@Component(
  selector: 'some-app',
  templateUrl: './some.component.html',
  styleUrls: []
)
export class SomeComponent implements OnInit 

  myObservable;

  constructor(private someService: SomeService)  

  ngOnInit() 
    this.myObservable = this.getSomething();
  

  getSomething() 
    return this.someService.getSomething();
  

然后,像这样 (documentation here) 将异步管道应用到您的模板:

<p> myObservable | async </p>

或者,正如 Optiq 所说(例如,当您处理对象时):

<div *ngFor="let a of myObservable | async">
  <p> a.whatever </p>
</div>

请注意,您不能将异步管道应用于函数。

3。收到数据后做一些事情

最后,您可以处理已收到数据的情况。见下文:

import  Component, OnInit  from '@angular/core'

@Component(
  selector: 'some-app',
  templateUrl: './some.component.html',
  styleUrls: []
)
export class SomeComponent implements OnInit 

  myObservable;

  constructor(private someService: SomeService)  

  ngOnInit() 
    this.myObservable = this.getSomething();
  

  getSomething() 
    const obs = this.someService.getSomething();
    obs.subscribe((data) => 
      // Handle success
    , (err) => 
      // Handle error
    , () => 
      // Make some function call
    )
    return obs;
  

【讨论】:

【参考方案4】:

我会根据你的代码以更通用的方式回答

foo.service.ts

@Injectable()

export class FooService 

    constructor(private _http:HttpClient) 

    public getFoo():Observable<any> 
        return this._http.get(`$API_URL`).map(r => r.json());
    

在地图运算符中,您可以进行操作并返回新状态

foo.component.ts

导入 rxjs finally 操作符在 final 调用方法

import 'rxjs/add/operators/finally';


export class FooComponent 

    public bar$:Obervable<any>;

    constructor(private _foo:FooService)

    getBar(): void 

        this.bar$ = this._foo.getFoo()
                             .finally(() => 

                               // INVOKE DESIRED METHOD

                             );

    

foo.html

<div *ngIf="bar$ | async; let bar"> <h3> bar </h3> </div>

这将创建 div 包装元素并为其提供可见性隐藏选项,因此不需要 ?。 “猫王”运算符

您也可以根据自己的需求修改 ngIf 表达式

【讨论】:

感谢您的意见维克多。是的,我猜map 运算符是数据转换的好地方。但是,在某种情况下,您的看法是什么,有时我会从 subscribe's completion observer 进行函数调用,即当我知道我的组件中有数据可用时调用函数。当我使用async 时,我该如何做同样的事情。 糟糕,我完全忘记了这一点。我更新了答案。 谢谢,维克多...我会尝试使用异步并告诉你。【参考方案5】:

优点:

    它会自动订阅 && 取消订阅组件 Init && 破坏。不再使用 subscribe() 和 unsubscribe() :) 您不必使用 ?(elvis 运算符) 代码行更少。

注意:你一定会喜欢的,代码仅供参考。请逐步实现以下代码以使用您的 API 数据进行测试。

// call your imports
@Component(
   selector: 'some-app',
   template: '
     <ng-container *ngIf="data$ | async as data">
         <input [value]="data.new" />
         <div *ngFor="let val of data">
            // some more todo things
         </div>
     </ng-container>
   '
)
export class SomeComponent 
     data$: Observable<data>;

【讨论】:

以上是关于尝试在我当前的 Angular 应用程序中使用异步管道而不是订阅的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 (ng2) 在 [class.whatever] 中使用异步管道

Angular 中的异步数据处理

Angular 打字稿异步 HTTPClient 调用

Angular 订阅无法按我的预期工作

在角度脚本标签中使用异步时手动引导角度

如何从 Angular 中的异步输入中获取“就绪”值