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-outlet
  • region - So the routes that sit below region (city and area) 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.