我已经阅读了许多有关如何使用http客户端在服务中实现http调用的Angular教程,那很好。当我尝试将数据和http调用分离到两个不同的服务中时,问题就来了,由于可观察性和订阅性以及在体系结构方面,我感到困惑。
请检查以下示例:
我将DataService用作数据的中心点,以便可以从不同组件访问它。当ListComponent请求项目列表时,DataService调用http服务以从服务器获取数据。 http服务中的此操作是异步的,因此它返回一个Observable。
我已经使用主题和订阅实现了它:
export class ListComponent implements OnInit, OnDestroy {
private listChangedSubscription: Subscription;
ngOnInit() {
this.list = this.dataService.getList();
this.listChangedSubscription = this.dataService.listChangedSubject.subscribe(
(list: Array<Item>) => {
this.list = list;
});
ngOnDestroy() {
this.entriesChangedSubscription.unsubscribe();
}
export class DataService {
listChangedSubject = new Subject<Array<Item>>();
private list: Array<Item> = [];
private fetchItems() {
return this.httpService.getItems().subscribe(
(list: Array<Item>) => {
this.list= list;
this.listChangedSubject.next(this.list.slice());
}
);
}
export class HttpService {
public getItems(observe?: 'body', reportProgress?: boolean): Observable<Array<Item>>;
public getItems(observe?: 'response', reportProgress?: boolean): Observable<HttpResponse<Array<Item>>>;
public getItems(observe?: 'events', reportProgress?: boolean): Observable<HttpEvent<Array<Item>>>;
public getItems(observe: any = 'body', reportProgress: boolean = false ): Observable<any> {
我认为在DataService中使用 Observables或Promises 而不是Subject和Subscription可能是一种更好的方法,但是我无法使其正常工作,有关fetchData()的任何建议功能?
在所有教程中,我都阅读了直接调用http服务的组件,我认为这仅对简单的情况有效,即组件保留其数据并且不需要数据服务。
推荐的方法是什么?
谢谢!
丹妮
答案 0 :(得分:1)
好的,所以我想我更好地理解了, 我认为实现此目标的最佳方法仍然是使用Subjects的dataService。真的,这就是RXJS最好的。
列出组件模板:
<ul *ngFor="let item of list">
<li (click)="onClick(item)">
<span>{{ item.date } </span>
<span>{{ item.text }}</span>
</li>
</ul>
export class ListComponent implements OnInit {
list: Item[] = [];
ngOnInit() {
this.httpService.getItems().subscribe(val => {
this.list = val;
});
}
onClick(item: item): void {
this dataService.updateItemListSubject(item);
// navigate to route
}
}
请注意,您不再需要onDestroy和订阅列表。 HttpClient将为您做到这一点
export class DataService {
private itemSubject = new Subject<Item>();
getItemSubject(): Observable<Item> {
return this.itemSubject.asObservable();
}
updateItemSubject(item: Observable<Item>): void {
this.itemSubject.next(item);
}
}
ItemDetail组件模板
<div>
{{item$ | async}}
</div>
export class ItemDetail implements OnInit {
item$: Observable<Item>
ngOnInit() {
this.item$ = this.dataService.getItemSubject();
}
}
export class HttpService {
// normal stuff
}
很抱歉这么长。
让DataService进行Http调用可能会变得很复杂,并且需要DataService更加了解应用程序。这就是为什么我在父组件中进行Http调用,然后将响应分配给DataService中的值的原因,如上所示。
使用RXJS的优点是,无论何时通过异步管道或通过订阅使用数据的任何地方,都会得到通知。它还可以避免您担心从DataService获取列表并意外执行list.splice(2)
的情况,并且由于您忘记了列表在任何地方都被使用而破坏了一切。
答案 1 :(得分:0)
非常感谢您对安德鲁的回应!
我也发现这篇文章很有意义:
答案 2 :(得分:0)
在 Angular 工作几年后,我想我可以回答我自己的问题:-)
<块引用>我认为使用 Observables 或 Promises 可能是更好的方法 数据服务而不是主题和订阅,但我不 让它工作,对 fetchData() 函数有什么建议吗?
在 Angular 中在服务中使用 Subject 是很常见的,因此您可以发出值并且客户端可以订阅。对于后者,通常会像这样添加一个属性或方法:
const itemsSubject = new Subject<any>();
const items$ = itemsSubject.asObservable();
或
function getItems(): Observable<any> {
return itemsSubject.asObservable();
}
对于发出 http 请求的数据服务的特殊情况,可以使用 BehaviourSubject 或 ReplaySubject,以便在请求完成后订阅的客户端可以获得先前的响应。
<块引用>在所有教程中我都读过组件直接调用 http 服务,我认为只对以下简单场景有效 组件保存其数据并且不需要数据 服务。
推荐的方法是什么?
Angular 组件的主要目的是处理视图的呈现。一旦获取或处理数据变得复杂,强烈建议将其提取到服务中。这样可以提高组件的可重用性并提高可测试性。
将此作为一般性建议,并根据具体情况做出选择。
这里有一个可以作为参考的帖子列表:
https://www.digitalocean.com/community/tutorials/rxjs-subjects
https://medium.com/@benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
另外我建议阅读 Angular 和 RxJS 的官方文档:
https://angular.io/guide/observables-in-angular
答案 3 :(得分:0)
如果您编写自己的服务而不使用 swagger 或 ... ,它会更好更干净。您可以创建一个通用服务并像 Item 一样在您的模型上调用它,并防止使用 any 来获得打字稿和智能感知的能力。下面是一个例子:
服务:
@Injectable()
export class GenericService {
constructor(private _http: HttpClient) { }
get<T>(url: string): Observable<T> {
return this._http.get<T>(url);
}
post<T,U>(url: string, body: U): Observable<T> {
return this._http.post<T>(url, body);
}
put<T,U>(url: string, body: U): Observable<T> {
return this._http.put<T>(url, body);
}
delete<T>(url: string): Observable<T> {
return this._http.delete<T>(url);
}
}
组件
@Component({
selector: ''
...
providers: [GenericService]
})
export class ListComponent implements OnInit {
list: Item[] = [];
constructor(private _service: GenericService)
ngOnInit() {
this._service.get<Item>(url).subscribe(val => {
this.list = val;
}
}
}
通过使用这种方法,您可以删除您的服务文件和您需要调用 api 的地方,将通用服务注入组件并调用您想要的 http 方法。您可以通过在 GenericService 中的 httpClient 方法上调用 toPromise() 将 Observable 转换为 promise。