Angular跨服通信

Angular cross-service communication

我有一个统计应用程序。在我页面的左侧,我有主题列表,在顶部 - 组列表。主要部分包含与主题和组相关的统计项。

我还有几个服务可以为我的应用程序提供业务逻辑。为简单起见,让我们谈谈其中三个:ThemeSerivce、GroupServiceStatisticsService.

最终用户可以操作主题和组列表(添加或删除项目),每次更改后我都必须重新计算统计信息。在此应用程序中,我使用 SubjectsSubsription 来自 rx.js 来跟踪此类更改.

所以,在我的组件中我可以这样写:

对于 GroupComponent

 removeGroup() {
        this.groupService.removeGroup(this.group);
        this.statisticsService.updateStatistics();
      }

对于 ThemeComponent

removeTheme() {
    this.themeService.removeTheme(this.theme);
    this.statisticsService.updateStatistics();
  }

但从逻辑上讲,这些组件不必了解统计信息。当然,我可以将 StatisticsService 的依赖项移动到 ThemeServiceGroupService 中,但之后我将不得不调用statisticsService.updateStatistics() 在每个改变主题或组集合的方法中。这就是为什么我想通过订阅实现直接的跨服务通信。

最后是我的问题:

  1. 这是个好主意吗?

  2. 如果可以的话,最好的实现方式是什么? 当我在组件中使用 Subscription 时,我在 ngOnInit() 方法中注册它并在 ngOnDestroy()[=50= 中取消订阅] 以防止内存泄漏。 我可以在服务的构造函数中订阅它吗?我应该何时何地退订?或者当我在 App 模块级别将我的服务注册为提供商时,也许没有必要?

StatisticsService 应订阅主题和组列表。您的各个组件应该只订阅它们各自的服务(ThemeComponent 到 ThemeService,Group 到 Group 等)。

为简单起见,我只关注 ThemeService,但 GroupService 是相似的。 ThemeService 应该有一个内部 Subject 当调用 remove 时,Subject 将是下一个值(可能是整个新列表)。

StatisticsService 将订阅 ThemeService observable 并在更改后重新计算。它看起来像这样

/* theme.service.ts */
@Injectable()
export class ThemeService {
    private _list$ = new Subject<ThemeList>();

    get list(): Observable<ThemeList> {
        return this._list$.asObservable();
    }

    set list(newList: ThemeList) {
       this._list$.next(newList);
    }
}


/* statistics.service.ts */
@Injectable()
export class StatisticsService {
    private _calculation$ = new Subject<StatisticResult>();

    constructor(private themeService: ThemeService) {
        themeService.list.subscribe((themeList: ThemeList) => this.updateCalculation(themeList));
    }

    get calculation(): Observable<StatisticResult> {
        return this._calculation$.asObservable();
    }

    updateCalculation(newData: ThemeList | GroupList) {
        // ... do stuff
        this._calculation.next(statisticResult);
    }
}


/* statistics.component.ts */
@Component({
  selector: 'statistics',
  template: '<p>{{ statisticResult$ | async }}</p>'
})
export class StatisticsComponent {
    statisticResult$: Observable<StatisticResult>;

    constructor(private statisticsService: StatisticsService) {
        this.statisticResult$ = statisticsService.calculation;
    }
}