尝试在我当前的 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 the
completionobserver
(如上所示),并且通过这样的操作,我不确定我将如何这些操作一旦切换到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 transformation
和 error 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 应用程序中使用异步管道而不是订阅的主要内容,如果未能解决你的问题,请参考以下文章