使用数据服务集中化Angular中的数据和http调用

时间:2019-03-02 03:01:50

标签: angular

我已经阅读了许多有关如何使用http客户端在服务中实现http调用的Angular教程,那很好。当我尝试将数据和http调用分离到两个不同的服务中时,问题就来了,由于可观察性和订阅性以及在体系结构方面,我感到困惑。

请检查以下示例:

  • 1 ListComponent-显示项目列表
  • 1 DataService-用于数据,具有项目列表和获取,添加,更新,删除等功能。
  • 1 HttpService-用于Http调用,由Swagger for Angular自动生成。

我将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服务的组件,我认为这仅对简单的情况有效,即组件保留其数据并且不需要数据服务。

推荐的方法是什么?

谢谢!

丹妮

4 个答案:

答案 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)

非常感谢您对安德鲁的回应!

我也发现这篇文章很有意义:

https://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/

答案 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://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/

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

https://rxjs-dev.firebaseapp.com/

https://www.learnrxjs.io/

答案 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。