Angular – Nested Components

Introduction

Components are building blocks of angular applications.

Splitting the functionality in smaller components is something very common in angular applications development.

In this post we will learn about nested components and discover how to establish communication between nested component and its container component using two useful angular directives @Input and @Output.

We’ll be using the same code-base from the earlier posts and adding more components to it.

Component Usage

Components are typically used in following two ways.

  • As a Routing Target
    • It appears to the user they’ve navigated to another view.
    • Template is displayed in a full page style
  • As a directive ==> Custom HTML syntax (aka selector)
    • This is same for nested components.

Following picture shows how a child (DeviceCard) component is placed inside a Parent (Devices) component.

When components have such relationship, we can establish communication using @Input and @Output directives.

  • Pass data to a nested-component using @Input.
  • Receive an Event from nested-component using @Output.

Passing Data to a Nested Component with @Input

@Input() decorator marks a field as an input:

It is basically saying that, for this field, data will be coming from parent. It receives the data before calling the ngOnInit() method, so we can use it in ngOnInit() method.

The following picture shows how this nesting is implemented:

devices.component (parent) is nesting the device-card.component (child).

Angular has different types of interactions between components, but @Input() is used to make the interaction between parent-child types of component.

There are various ways we can interact with one component to another components. Some methods you can use to communicate the component include:

  • @Input()
  • @viewChild()
  • Using shared services : Between Sibling or Unrelated Components:

@Input() is usually used when the hierarchy level between parent and child is small. If there is a larger hierarchy, Its better to use a service.

Passing Data from a Nested Component with @output

Passing data to parent, is typically done in response to some event on the child component like a button click and the communication is made possible using @Output decorator.

The @Output decorator emits an event up to a parent component using event binding. The parent component should have a function that accepts the data emitted from the child component. It gets the data before calling the ngOnInit() method.

So, the @Output decorated property is going to be an Event Emitter.

Let’s first update the child component as follows

A note about EventEmitter

We saw that if we need to send the data from the child component, it is possible by using the @Output() decorator and EventEmitter class.

EventEmitter is a class which is used to emit custom events and is used only with the @Output() decorator. It extends the Subject class, which is used to apply the provider-subscriber kind of communication. Subject is a part of Observable, and you can read more about it here.

next, lets update the parent’s code as follows:

The parent component should have a function that accepts the data emitted from the child component

with all this in-place, if we now click the button on child component, will see following output:

Nesting Components

Lets update our example to work with multiple devices data.

I’ve updated the code to work with devices array and used *ngFor of loop to render multiple Childs.

and now we can start seeing the reusability applied and proper communication between parent and child.

Nesting another Component

Lets create another child component device-create.component.

This component will render a data entry form for new device data. We will add this component as a child of devices.component (which currently rendering the device cards).

Logic is simple; we will add a Register button which will control show/hide of device-card list or device-create component . We will be using @output decorators to send event data from the device-create (child) to parent:

Cancel button flow

Following is the wiring for above shown cancel button behavior. It emits an event to parent and parent toggle the visibility (addMode=false)

Create Device button flow

UI enters into addMode when Register button is clicked which displays a data entry form (Register New Device):

User enters the data into form and click Create Device button (No. 2 above), which emits data to parent and parent update the devices array, also toggles the display which in-turn display cards for all devices (including newly added).

Following picture shows the child components sending new data to parent, communication flow:

Using the Angular Shared Service

as our application grows and our components get more complex with deep nesting over time, it then becomes difficult to manage the data flow using these @Input/@Output decorators.

For such scenarios, it is more efficient to pass data around between components using a shared service. A service is simply an Angular typescript class that acts as a central repository.

Let us update our example to use such a shared service to pass data, instead of using property/event binding.

Note above that we are returning a copy of the “devices” inside getDevices() method using slice() and not the original array so that it cannot be modified outside this service.

Now that we have the “devices” array/model as part of the service, let’s remove it from our DevicesComponent. Inside DevicesComponent, we’ll get access to the devices using the getDevices() method defined in our DeviceService:

if we now test our application, it shall still be working like before.

However, we’ll also update the code for event handling to be via services. To do this, we’ll add “deviceClicked” as a property on the DeviceService:

We can now inject this service into device-card.component (child) via constructor and use it as shown below clicked() method:

now, we can subscribe for the above deviceClicked event in parent component or any other component. For demo purpose, we’ll subscribe it inside the App.component which is the top level container.

we can see that using shared angular services is another approach to pass data around between components instead of manually passing them.

Our individual components can subscribe to parts of this data store, which greatly reduces the burden on parent/child components in our app to pass data back and forth.

Angular services can contain all the business logic which is entirely independent of the components and we can consume them across multiple components. These can act as a bridge between two components i.e. Sender component and Receiver component.

With this we’ll end the post.

Summary

In this post we explored passing data between parent-child kind of nested components. We learned about @Input() and @Output decorators and implemented few components to demonstrate these functionalities.

You can check the source code from this git repository.

The deployed angular application can be visited from this link.

Let me know if you have some questions or comments. Till next time, Happy Coding.