【问题标题】:Can I create an instance of a service for every loaded component in Angular?我可以为 Angular 中每个加载的组件创建一个服务实例吗?
【发布时间】:2021-05-19 18:10:36
【问题描述】:

我有一个名为 edit-order.component 的组件,其中包含以下 ts 文件

export class EditOrderComponent implements OnInit {

  constructor(private editOrderSrv: EditOrderService, private route: ActivatedRoute) { }

  ngOnInit(): void {
    this.editOrderSrv.getOrderById(this.route.snapshot.params.id);
  }
}

在我的edit-order-service.service.ts 我有以下代码

export class EditOrderService {
  order$: BehaviorSubject<any> = new BehaviorSubject(null);
  constructor(private http: HttpClient) { }

  getOrderById(id: string){
    this.http.get('url', {params: {id}}).subscribe(order => {
      this.order$.next(order);
    }, error => {console.error(error)});
  }
}

客户端应该能够打开多个edit-order.component 和不同的id。在我的child.component,我想像这样订阅order$

export class ChildComponent implements OnInit {
  order: any;
  constructor(private editOrderSrv: EditOrderService) { }

  ngOnInit(): void {
    this.editOrderSrv.order$.subscribe(order => {
      if(order) this.order = order;
    })
  }

}

但因此我必须为每个edit-order.component 拥有自己的服务实例,以免覆盖order$。我将如何实现这一目标?或者这是不可能的?如果不可能,我还能做些什么来实现我的目标?

【问题讨论】:

  • 为什么要使用单独的BehaviorSubject?您可以只返回 getOrderById() 返回的 observable。
  • @rveerd 稍后。我有多个 edit-order.component 的子组件,并且我必须在服务中为该特定订单存储多个变量。这就是为什么我需要不同的实例来不覆盖变量,以便我可以访问相应订单的正确变量。
  • 如果您使用的是角度路由,那么您可以在 id 的基础上使用相同的组件,也可以使用相同的服务实例。当前的实现对我来说似乎有点不寻常。
  • @Invader 我认为您不应该在服务中存储特定于组件的数据。将其存储在组件中。仅将服务用于“服务”,例如从 Web 服务获取数据。
  • 如果您确实需要每个组件实例的服务,您可以使用dependency providers 来实现。

标签: angular typescript service


【解决方案1】:

好奇,为什么不是你的功能是

getOrderById(id: string){
    return this.http.get('url', {params: {id}})
  }

你订阅了getOrderById

如果你愿意,你可以通过管道(catchError)

更新您也可以在服务中使用“缓存”而不使用主题数组,只使用对象数组

  orders:any={}  //create an empty object where we store the result

  getOrderById(id: string){
    if (this.orders[id])
        return of(this.orders[id]

    return this.http.get('url', {params: {id}}).pipe(
      tap(res=>{
         this.orders[id]=res;
      })
    )
  }

【讨论】:

  • 我尝试最小化 http 请求的数量,我需要子组件中的顺序,这就是为什么我想调用该函数一次,然后总是只订阅 order$ 在其中的任何子组件中我需要订单,否则我会在不同的子组件中多次调用该函数。我想以某种方式集中订单。我不太确定有多少 http 请求会好起来,这就是为什么我尝试用尽可能少的请求做所有事情,然后在整个应用程序中使用数据。
  • 我将答案更新为使用“缓存”或使用pipe(tap)的订单
  • @Eliseo 我也考虑过这个解决方案,但是,假设您有 2 个具有相同 id 的组件,并且当您从第一个组件更改数据时,您不会在第二个组件中收到通知。跨度>
  • ::glups:: 你比我想的更深,没错,这种情况下不行
【解决方案2】:

创建不同的服务实例是一个非常糟糕的主意。该服务的目的是单例,这就是它的设计方式。 您可以使用它的一个好方法是为每个可观察的订单保存一个键值对象。从父组件将this.route.snapshot.params.id 作为输入传递给子组件。 (代码未经测试

服务:

export class EditOrderService {
   public orders$: { [orderid: string]: BehaviorSubject<any> } = {};

   constructor(private http: HttpClient) { }

   getOrderById(id: string): void {
      this.http.get('url', { params: { id } }).subscribe(order => {
         if (!this.orders$[id]) {
            this.orders$[id] = new BehaviorSubject<any>(null);
         }
         this.orders$[id].next(order);

      }, error => { console.error(error); });
   }
}

子组件:

export class ChildComponent implements OnInit {
  order: any;
  @Input() orderId: any;
  constructor(private editOrderSrv: EditOrderService) { }

  ngOnInit(): void {
    this.editOrderSrv.orders$[orderId].subscribe(order => {
      if(order) this.order = order;
    })
  }

}

【讨论】:

  • 这是个好主意,没想到这个。因此,我只需创建一个对象,其 id 作为我要为订单存储的每个数据的键。我会试试谢谢!
  • 这点是你很难解决的问题,但不是创建服务实例,而是创建 BehaviorSubject 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-21
  • 1970-01-01
  • 2018-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
相关资源
最近更新 更多