Sharing data between child and parent components in Angular
15th Dec 2023
Consider the following set of parent and child relationships in an Angular application with Angular's standard routing:
/
├── region
│ └── city
│ └── area
├── Help
└── Account page
Assume we have a <router-outlet>
directives in the following:
/
- Home - so this is the "master" router-outletregion
- So the routes that sit belowregion
(city
andarea
) will be rendered here
How do we pass a value that is resolved in the area
route back up the home page?
In an ideal world
In an ideal world we'd be able to remodel our entire html structure so that it roughly followed the routing and we would not need to pass data back up to the parent. This solution is for those situations where you just can't.
How not to do it
If you attempt to retrieve the value in the homepage, you'll hit problems near enough no matter what you do.
Subscribing to router events in the homepage will cause you pain as they will be fired for every child route in a path when there is a transition. For example, if you land on home/region/city/area
, the NavigationEnd
event will be fired three times - once for region, once for city and once for area.
Whilst this will still let you access the route data for area when the NavigationEnd
event is fired for it, the NavigationEnd
for the area
route will be fired first, followed in quick secession by the NavigationEnd
events for the city
and the area
routes.
So if you manage to get hold of the data from the area
route, you need to be careful not to lose that data when the event fires for the parent routes.
How to do it
This is where angular services can help us. Angular services are singletons, and exist in memory for the duration of an Angular app being loaded and running.
Create a session service and register it with you app module:
ng generate service app/services/SessionService
And next, let's create a property in our new SessionService to hold a reference to our area:
export class SessionService {
public area: Area | undefined;
...
}
Now, we can update our area
component to write it's resolved data into the session service:
this.route.data.subscribe(data => {
this.sessionService.area = data['area'];
});
We then also need to clean up after ourselves, and remove the data from the session when we destroy the area
component:
ngOnDestroy () : void {
this.sessionService.area = undefined;
}
Next, we can import the SessionService into the home
component and start accessing the value in our html buy using the protected
modifier:
constructor (
protected session: SessionService
) { }
And now in our html for the home
component:
<SomeChildComponent
[area]=session.area>
</SomeChildComponent>
And we have now passed our area all the way back up to our home component and consumed it!
How does this work
Wonderfully, this works because our session is an object - so by passing it around in this way, we are simply passing around a reference in memory to the same object.