Angular components are the building blocks of any Angular application. Components provide the views or templates for your application, as well as, manage the data bound to those views.  Some of most common data sharing scenario is in which two or more components share information are:

  1. Parent Component => Child Component : @Input — property binding
  2. Child Component => Parent Component : @Output & EventEmitter  via event binding
  3. Unrelated Or Sibling Components :  Using Shared Service

Let’s look at ways to share data in each of these scenarios.

For the sake of simplicity I’ve removed most of the unrelated code that’s needed for this example to run. You can download complete code for this example from my GitHub.

Parent to Child: Sharing Data using @Input decorator

This is probably the most common and straightforward method of sharing data. It works by using the @Input() decorator to allow data to be passed via the template. @Input decorator provide a mechanism to allow a parent component to bind its properties to child component there by giving child  access to its data. These binding should point to properties available in the parent component. In this example we will pass “parentMessage” property of MyParentComponent to MyChildComponent.

my-parent.component.ts

parentMessage: string = "Parent Says Hello!!!!";

ParentComponent view nests the ChildComponent selector, binding its  “parentMessage” string property to the child’s “message” alias. In the parent component’s template, simply define property bindings in the child’s selector.

my-parent.component.html

<app-my-child [message]="parentMessage"&gt;</app-my-child&gt;

Now, in the child component, define input property “message” with the @Input decorator.

my-child.component.ts

@Input() message: string;

Child component now has access to the value of “parentMessage” from the parent component. We can now use “message” property of child component to bind to child’s view.

my-child.component.html

<div&gt;
  <strong&gt;Message From Parent: </strong&gt;{{ message }}
</div&gt;

In case of large applications, while passing data with Input property binding method shown above user needs to be aware of and handle change detection for data properties to keep application performance optimum.

I have explained angular’s change detection strategies in a separate post titled “Input Property Binding and Change Detection Strategy in Angular”

Child to Parent: Sharing Data via Output() and EventEmitter

With @Output and EventEmitter , you can emit data back from the child to  parent component. Child component exposes an EventEmitter property which emits data when action/event happens at child component. Child’s EventEmitter property is an output property, typically adorned with an @Output decorator.

my-child.component.ts

 @Output() childMessage = new EventEmitter<string&gt;();

We then create a function that calls emit on this EventEmitter property with the data we want to send. Call to this function is triggered by events at child component.

my-child.component.html

<button class="btn btn-primary mt-4" (click)="updateParent()"&gt;
  Send Message To Parent
</button&gt;

my-child.component.ts

updateParent() {
   this.childMessage.emit('Hello! My Parent !!!');
 }

The parent now binds to that event outputted by child component in its view at child component’s selector and reacts to those events using handler function. Value emitted by the child component is passed to the handler function at parent component as the child event payload $event.

my-parent.component.html

<app-my-child [message]="parentMessage" 
   (childMessage)="showMessageFromChild($event)"&gt;
</app-my-child&gt;

The framework passes the event argument—represented by $event—to the handler method, and the method processes it.

my-parent.component.ts

showMessageFromChild(message: any) {
   this.messageFromChild = message;
}

As shown below, “Send Message To Parent” button which is part of Child Component when clicked will display Child’s message to parent at Parent Component view.

 

 

Sharing data between parent-children-grandchildren using @Input/@Output decorators helps make your components independent, assuming it is a pure component, that relies completely on the Input received and produces an Output which is definitive. This approach makes components, easily testable and reusable.

However, as your application becomes more modular and components become more complex and nested over the time it becomes complex to manage data flow using @Input/@Output decorators.

With routing and unrelated or sibling components, sharing data with @Input/@Output decorators is not possible every time. In such cases its more efficient to share data between components using a shared service.
When passing data between components that lack a direct connection, and when you have data that should always been in sync, using RxJS Subject and BehaviorSubject is very useful.

Unrelated Components: Sharing Data with a Service

Parent component, its children and siblings can use a shared service whose interface enables bi-directional communication of data between components. We will use Subject to create our shared service in this example. Subjects are observable’s themselves but what sets them apart is that they are also observers. It means that a subject can emit data, on top of having the capability to be subscribed to. Subjects are multicast, they support multiple subscriptions. As a result, you can use a Subject in a service to fetch some data and send the result to all components that subscribed to that Subject. 

In the service, we create a private Subject variable “myMessage” that will hold the current value of the message.

message.service.ts

export class MessageService {
    private myMessage = new Subject<string&gt;();
}

We then expose “myMessage” property as Observable through “getMessage()” method in our service.

message.service.ts

getMessage(): Observable<string&gt; {
   return this.myMessage.asObservable();
}

It’s not recommended to expose the Subject object directly to your components. Instead, return Observable version of it as shown above that will be used by the components. Next, we create function that calls next on the Subject to change its value.

message.service.ts

updateMessage(message: string) {
  this.myMessage.next(message);
}

We then inject the “MessageService” in the constructor component that wants to use this data and then subscribe to the “myMessage” observable returned by getMessage() method of service. Components subscribed to the observable stream will receive updates on the value of “myMessage” , store it in local component variable that is then used to bind it to component’s view .

my-parent.component.ts

constructor(private messageService: MessageService) {
  this.subscription = this.messageService.getMessage()
  .subscribe(mymessage => this.messageFromSibling = mymessage)
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}

Notice above in the example we capture the subscription at constructor. Do not forget to implement OnDestroy at your component and unsubscribe from the subscription at “ngOnDestroy ()”. This is a memory-leak guard step. There is no actual risk in this app because the lifetime of components in this app are the same as the lifetime of the app itself. That would not always be true in a more complex application.

my-parent.component.html

<p class="mt-5" *ngIf="messageFromSibling"&gt;
  <strong&gt;Message From App Component: </strong&gt;<br&gt;
    {{ messageFromSibling }}
</p&gt;

If component want’s to update the current value of Subject we can inject service at constructor and call on updateMessage() method of our service to set the next value as shown below.

app.component.ts

 constructor(private messageService: MessageService) {
  }

 ngOnInit() {
    console.log("At app component");
    this.messageService.updateMessage('Hello! From AppComponent !!');
 }

In video below you can see that initial message is set by App Component that is displayed in view of Parent, Sibling and Child Component as all these components have subscribed to Subject Observable. On “Update App Message” button click new value is set on subject which in turn will update message for all subscribed components.

 

 

There is a one more method to access child component data from parent component by using @ViewChild and AfterViewInit. With this option, we can refer to a child component and access their variables inside our parent component. @ViewChild allows a one component to be injected into another, giving the parent access to its attributes and functions. One caveat, however, is that child won’t be available until after the view has been initialized. This means we need to implement the AfterViewInit lifecycle hook to receive the data from the child.

We will try to cover use of @ViewChild sometime in a different post in future.

Share this:

Leave a Reply

Your email address will not be published. Required fields are marked *