如何管理角组件中的大量行为主题

发布于 2025-01-19 06:05:31 字数 1886 浏览 1 评论 0 原文

目前我正在开发一些模块,在该模块中我们尝试使用异步管道而不是markForCheck等方法。 大多数数据都是通过 HTTP 传输的,整个项目的方法是使用 ChangeDetectionStrategy.OnPush。

通常,我们所有的 BehaviourSubjects 都在我们的“智能”组件中,并且表示组件的值由输入设置。这对于较小的模块来说很好。我们通常有 2-6 个这样的演示组件。对于更大的、有更多 api 调用的系统,我们会被越来越多的来自行为主体的代码弄得混乱不堪。比如:

  data1$ = new BehaviorSubject<Data1[]>([]);
  data2$ = new BehaviorSubject<Data2[]>([]);
  data3$ = new BehaviorSubject<Data3[]>([]);
  loading1$ = new BehaviorSubject<boolean>(true);
  loading2$ = new BehaviorSubject<boolean>(true);
  loading3$ = new BehaviorSubject<boolean>(true);
// and so on

然后我们用某种方法填充数据,比如

this.service.getData1()
.pipe(finalize() => this.loading1$.next(false))
.subscribe(result => this.data1$.next(result);

在更大的模块中,我们有许多这样的方法和许多 BehaviorSubjects 的声明。那么问题来了,有没有办法清理我们的组件呢?因为现在我们可能摆脱了更改检测的手动调用,但我们从行为主体中得到了一些混乱的代码。

我们考虑为行为主体添加一个新的服务。行为主体似乎经常从事其他服务。所以我们最终会得到像

export class BsService {
  private data1$ = new BehaviorSubject<Data1[]>([]);
  private loading1$ = new BehaviorSubject<boolean>(true);
//... other declarations

getData1Observable$(): Observable<Data1[]> { // this is for async pipe
return this.data1$.asObservable();
}
setData1(data:Data[]){this.data1$.next(data)}
getData1(): Data1[] {return this.data1$.value} //we sometimes need direct access to value
getLoading$(){...}
setLoading(loadin:boolean){...}
pending$() {return combineLatest([...])};
//...and so on

这样的服务,这似乎只清除了 BehaviorSubjects 的声明,也许还有我们与 combineLatest 一起使用的一些方法。设置数据的其余方法将在我们的“智能”组件中保持不变。因此,我们应该将 BsService 添加到组件的构造函数中

constructor(...,service: DataService, behaviorSubjectService: BsService)

DataService 是组件和 API 服务之间的一层),

这是正确的方法吗?或者也许有更好的方法?

Currently I'm working on some module in which we try to use approach with async pipes instead of markForCheck and so on.
Most of data is coming over HTTP, and approach in whole project is to use ChangeDetectionStrategy.OnPush.

Usualy all ours BehaviourSubjects are in our 'smart' component and values for presentational components are set by inputs. This was fine for smaller modules. We usualy have 2-6 of such presentation components. For bigger ones, that have more api calls, we get cluttered with more and more code from behaviour subjects. Like:

  data1$ = new BehaviorSubject<Data1[]>([]);
  data2$ = new BehaviorSubject<Data2[]>([]);
  data3$ = new BehaviorSubject<Data3[]>([]);
  loading1$ = new BehaviorSubject<boolean>(true);
  loading2$ = new BehaviorSubject<boolean>(true);
  loading3$ = new BehaviorSubject<boolean>(true);
// and so on

Then we populate it with data in some method like

this.service.getData1()
.pipe(finalize() => this.loading1$.next(false))
.subscribe(result => this.data1$.next(result);

In bigger modules we have many of these method and many of declaration of BehaviorSubjects. So the question is, is there a way to clear up our component? Because right now maybe we get rid of manual calls for change detection but we get some cluttered code from behaviour subject.

We consider adding a new service for behaviour subject. It seems like often behaviour subjects goes in other service. So we would end up with service like

export class BsService {
  private data1$ = new BehaviorSubject<Data1[]>([]);
  private loading1$ = new BehaviorSubject<boolean>(true);
//... other declarations

getData1Observable$(): Observable<Data1[]> { // this is for async pipe
return this.data1$.asObservable();
}
setData1(data:Data[]){this.data1$.next(data)}
getData1(): Data1[] {return this.data1$.value} //we sometimes need direct access to value
getLoading$(){...}
setLoading(loadin:boolean){...}
pending$() {return combineLatest([...])};
//...and so on

So this seems to clear only declaration of BehaviorSubjects, and maybe some methods we use with combineLatest. Rest of the methods of setting data will stay the same in our 'smart' component. So we should add BsService to constructor of our component

constructor(...,service: DataService, behaviorSubjectService: BsService)

(DataService is a layer between component and API service)

Is this correct way of doing this? Or maybe there is better way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夜无邪 2025-01-26 06:05:31

每组数据(data1、data2 等)是否都来自 HTTP 调用?您可以摆脱BehaviorSubject并直接使用从HTTP调用返回的数据吗?

例如:

  products$ = this.http.get<Product[]>(this.productsUrl)
    .pipe(
      tap(data => console.log('Products: ', JSON.stringify(data))),
      catchError(this.handleError)
    );

上面是一个 products$ Observable,它提供了产品列表。您不一定需要将其发送到BehaviorSubject 中才能使用它。

如果您需要组合来自多个 HTTP get 的数据以在模板中显示,您可以将这些 Observables 与 combineLatest 组合起来,同样不需要BehaviorSubject。

  productsWithCategory$ = combineLatest([
    this.products$,
    this.productCategoryService.productCategories$
  ]).pipe(
    map(([products, categories]) =>
      products.map(product => ({
        ...product,
        category: categories.find(c => product.categoryId === c.id)?.name,
        searchKey: [product.productName]
      } as Product))
    ),
    shareReplay(1)
  );

上面的代码组合了一组产品和产品类别,以显示产品及其产品类别名称(而不是存储在产品数据中的 id)。

这样的东西对你有用吗?

Is each set of data (data1, data2, etc) coming from an HTTP call? Can you get rid of the BehaviorSubject and just directly work with the data returned from the HTTP call?

For example:

  products$ = this.http.get<Product[]>(this.productsUrl)
    .pipe(
      tap(data => console.log('Products: ', JSON.stringify(data))),
      catchError(this.handleError)
    );

Above is a products$ Observable that provides the list of products. You don't necessarily need to emit this into a BehaviorSubject to work with it.

And if you need to combine data from multiple HTTP get's for display in a template, you can combine these Observables with combineLatest, again without a BehaviorSubject.

  productsWithCategory$ = combineLatest([
    this.products$,
    this.productCategoryService.productCategories$
  ]).pipe(
    map(([products, categories]) =>
      products.map(product => ({
        ...product,
        category: categories.find(c => product.categoryId === c.id)?.name,
        searchKey: [product.productName]
      } as Product))
    ),
    shareReplay(1)
  );

The above code combines a set of products and product categories to display the products with their product category name (instead of the id stored in the product data).

Would something like that work for you?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文