问题
I have a statistics application. On the left side of my page I have list of themes, on the top - list of groups. The main part contains statistics items related to both theme and group.
Also I have several services which provide business logic for my application. For simplicity let's talk about three of them: ThemeSerivce, GroupService and StatisticsService.
End users could manipulate lists of themes and groups (add or remove items) and I have to recalculate statistics after each change. In this application I use Subjects and Subsription from rx.js to track such kind of changes.
So, in my components I could write something like this:
for GroupComponent
removeGroup() {
this.groupService.removeGroup(this.group);
this.statisticsService.updateStatistics();
}
for ThemeComponent
removeTheme() {
this.themeService.removeTheme(this.theme);
this.statisticsService.updateStatistics();
}
But logically these components don't have to know about statistics. Of course I can move dependency of StatisticsService into ThemeService and GroupService, but then I will have to call statisticsService.updateStatistics() in every method which changes collection of themes or groups. That's why I want to implement direct cross-service communication with subscription.
And finally my questions:
Is it a good idea at all?
If it's ok, what is the best way to implement it? When I use Subscription in components, I register it in ngOnInit() method and unsubscribe in ngOnDestroy() to prevent memory leaks. Could I subscribe it in service's constructor? And when or where should I unsubscribe? Or maybe it's not necessary when I register my services as provider on App module level?
回答1:
StatisticsService
should subscribe to the list of themes and groups. Your individual components should only subscribe to their respective services (ThemeComponent to ThemeService, Group to Group etc).
For simplicity, I'll just focus on the ThemeService
but GroupService
is similar. ThemeService
should have an internal Subject
when remove is called, the Subject
will next a value (probably the entire new list).
StatisticsService
will subscribe to the ThemeService
observable and recalculate after change. It will look something like this
/* 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;
}
}
来源:https://stackoverflow.com/questions/44975816/angular-cross-service-communication