This is my first post so I hope I'm doing things correctly.
I am trying to workout the best way to both use a resolver and share state between child route components and their parent route's component.
I have an Angular 2 application which has a component/route structure containing a parent route with two child routes as per the code sample below:
Routes:
export const personRoutes: Routes = [
{ path: 'home/:id', component: MyDetailsComponent,
resolve: {
person: PersonResolver
},
children: [
{ path: '', component: MyDetailsOverviewComponent, pathMatch: 'full' },
{ path: 'update', component: UpdateMyDetailsComponent }
]
}];
I am using a resolver on the parent route to fetch some data before it's view and subsequent child route views are loaded. The data is fetched from a service which makes an api call.
Resolver:
export class PersonResolver implements Resolve<any> {
constructor(private personService: PersonService) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Person> {
let id: string = route.params['id'];
return this.personService.getPerson(id);
}
}
Service:
getPerson(id: string): Observable<Person> {
let params = new URLSearchParams();
params.set('id', id);
return this.http.get(this.config.apiEndpoint + '/People/', { search: params })
.map((r: Response) => {
let person = r.json() as Person;
this.extractPersonData(person);
return this.currentPerson.asObservable();
})
.catch(error => {
console.log(error);
return Observable.throw(error);
}).share();
}
The service maps the data returned from the http call and uses it to provide the first value for a ReplaySubject which is then subscribed to by the parent component and child route components.
Here is the extractPersonData function called with the data returned from the http/api call/ this.currentPerson is the ReplaySubject with a buffer of 1 which the components will subscribe to.
extractPersonData(person: Person): void {
PersonService.cleanPersonData(person);
this.currentPerson.next(person);
}
I want to share the state of the person object between all three components and cannot use @Input/@Output variables as the child routes are dynamically loaded in the parent's router outlet.
Here is the code showing the parent and one of the child components subscribing to the ReplaySubject after the resolve:
Parent Component:
export class MyDetailsComponent implements OnInit {
private person: Person;
private subscription: Subscription;
constructor(private route: ActivatedRoute, private personService: PersonService, private modalService: NgbModal) {
this.route.data.subscribe((obs: Observable<Person>) => {
console.log(obs);
this.subscription = obs['person'].subscribe(person => {
this.person = person;
})
},
error => {
console.log('Home Component - Init person error: ' + error);
});
}
Child Component:
export class MyDetailsOverviewComponent implements OnInit {
private person: Person;
private subscription: Subscription;
isReady: boolean = false;
constructor(private route: ActivatedRoute) {
this.route.parent.data.subscribe((obs: Observable<Person>) => {
this.person = obs['person'];
this.subscription = obs['person'].subscribe(person => {
this.person = person;
});
});
}
My idea is to have an update method in the service which each of the components can call. The update method should call this.currentPerson.next(newPerson) internally and then the sibling child components and the parent component should be notified of changes caused by one another.
My question is whether this is a poor approach to sharing state across parent and child routes ? Is there an alternative way to doing this. At the moment I think I am leaking subscribers in the parent and child components and it feels wrong having a subscribe calls inside of another like this:
this.route.data.subscribe((obs: Observable<Person>) => {
console.log(obs);
this.subscription = obs['person'].subscribe(person => {
this.person = person;
})
Thanks.