【问题标题】:Trying to use Async pipe instead of subscribe in my current Angular application尝试在我当前的 Angular 应用程序中使用异步管道而不是订阅
【发布时间】: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

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

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

【问题讨论】:

    标签: angular typescript rxjs observable


    【解决方案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】:

      优点:

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

      注意:你一定会喜欢的,代码仅供参考。请逐步实现以下代码以使用您的 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>;
      }
      

      【讨论】:

        猜你喜欢
        • 2020-11-13
        • 1970-01-01
        • 2017-10-15
        • 2019-05-06
        • 2021-06-09
        • 1970-01-01
        • 2016-12-25
        • 2020-03-11
        • 2017-02-14
        相关资源
        最近更新 更多