Reputation: 338
I'm trying to make something that looks like this:
Looking for a way to make Service feed the latest data to subscribed components whenever its necessary. For example: Component3 writes changes to the API > Component1 and Component2 have to get the latest data.
Currently both components are subscribed to an observable returned by the service and this causes 2 API requests if both are rendered. This feels wrong. In my understanding these components should be fed the data and not request it on their own. To be clear: the data returned by this service is always being used in Component1 while Component2 is rendered optionally.
Here's some snippets and a bit of description of what I've done so far:
Service gets data through HTTP from API. Component1 and Component2 are subscribed to observable returned by Service:
import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
import 'rxjs/add/operator/map';
@Injectable()
export class GroupService{
constructor(private _http: Http){}
getGroups(){
return this._http.get('/assets/groups.json')
.map((res) => {
return res.json();
});
}
}
Component1 and Component2 are basically the same in terms of functionality. Here's one of them:
import { Component } from '@angular/core';
import { router } from '../../app.router';
import { GroupService } from '../../services/group.service';
import { Group } from '../../interfaces/group.interface';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss'],
})
export class SidebarComponent
{
router;
groups: Group[];
constructor(private _groupService: GroupService){
this.router = router;
this.getGroups();
}
getGroups(){
this._groupService.getGroups()
.subscribe((groups) => {
this.groups = groups;
});
}
}
Upvotes: 2
Views: 1584
Reputation: 13058
Using BehaviorSubject as a delegate is probably the most convenient option for your use case. Register the BehaviorSubject
in the service and subscribe to it in all components that need to be updated when any of those components requests new data.
This is how the service should look like:
@Injectable()
export class GroupService {
groupsSource: BehaviorSubject<any> = new BehaviorSubject(null);
constructor(private _http: Http) { }
getGroups(): void {
this._http.get('/assets/groups.json')
.map((res) => res.json())
.subscribe((groups) => this.groupsSource.next(groups)); // Update all components subscribed to the BehaviorSubjet
}
}
Component 1, requests new data:
export class Component1 implements OnInit {
groups: any;
constructor(private _groupService: GroupService) { }
ngOnInit() {
this._groupService.groupsSource.subscribe((groups) => {
this.groups = groups;
});
this._groupService.getGroups();
}
}
Component 2, gets updated automatically:
export class Component2 implements OnInit {
groups: any;
constructor(private _groupService: GroupService) { }
ngOnInit() {
this._groupService.groupsSource.subscribe((groups) => {
this.groups = groups;
});
}
}
With the BehaviorSubject
you can get the most recent value it holds without even subscribing to it:
export class Component3 implements OnInit{
groups: any;
constructor(private _groupService: GroupService) { }
ngOnInit() {
this.groups = this._groupService.groupsSource.getValue();
}
}
Upvotes: 3
Reputation: 6372
A slightly different approach from seidme's, that I would personally prefer:
Set up a data stream in GroupService
and use that in your components.
Service:
import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class GroupService {
private groupSource = new BehaviorSubject<any>({});
public group$: Observable = this.groupSource.asObservable();
constructor(private _http: Http) {}
getGroups() {
this._http.get('/assets/groups.json')
.map((res) => {
return res.json();
})
.subscribe((groups) => this.groupSource.next(groups));
}
}
Then in your components, just do this:
import { Component, OnInit } from '@angular/core';
import { router } from '../../app.router';
import { GroupService } from '../../services/group.service';
import { Group } from '../../interfaces/group.interface';
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss'],
})
export class SidebarComponent implements OnInit {
router;
groups: Observable;
constructor(private _groupService: GroupService){
this.router = router;
}
ngOnInit {
this.groups = this._groupService.group$;
}
}
Then in your template, use the async
pipe: {{ groups | async }}
. It will take care of subscribing to (and disposing of) the observable for you.
Now whenever you call GroupService.getGroups()
from any component, all the other ones will update automatically.
Also, using OnInit
is slightly preferable to doing stuff in the constructor, see Difference between Constructor and ngOnInit.
Upvotes: 5