How to communicate from one Angular component to another?

That’s a common question. The reason for that is a problem importance. It becomes even more important if you have a lot of components and need to notify them about changes in different places.

Redux does a good job on that, and I like it. But for most of the projects, where you don’t have a compound state, it might be overkill.

I have already written an article on the component communication for Angular1.5+ using $q promises. It’s an easy way which I’m still using for some of my projects.

You could ask: “What about Angular2+ applications?”

In this article, I will show to an even more precise and elegant solution available.

I will create a simple application with a notification component. The idea is that you could send notifications from any place of the web application and render it by our component.

Preparing the project

I will use Ng CLI to generate the project, its components, and serve it.

To install CLI tool and create the project run:

npm i -g @angular/cli
ng new ng-communicate-components
ng cd ng-communicate-components

Now we have the working directory ready, and we can start implementing our solution.

But hey, what solution are we going to implement? Right, I haven’t mentioned it yet.

If you noticed the word ‘Observables’ in the title to this post, you might realize that I will be writing about RxJS – a Reactive Extensions library for JavaScript.

Observables allow us to build asynchronous data streams into the app. Let’s check how we can create one.

First of all, I am going to generate a new component called ‘Notification.’ To do so, we just need to run a simple command: ‘ng g component notification’

Now we have a `notification` folder which contains HTML, CSS and JS files required for our component.

I already have CSS ready for the application. The idea is to show the notification in the bottom of the screen.

It will have a background color corresponding to its type. It might either be red (‘error’) or green (‘success’).

Here is a code for our template.

<div *ngIf="notification"
     [ngClass]="{
         'isSuccess': notification.type === 'success',
         'isError': notification.type === 'error'
     }"
    class="Notification u-paddingVertical-s text--bold">
    <div class="Container Grid Grid--spaceBetween Grid--alignCenter">
        <span>{{notification.message}}</span>
        <i class="icon-fh-close text--14px Btn--pointer"
           (click)="close();"></i>
    </div>
</div>

From the HTML markup, you can see that the notification object has two properties: type and message. Knowing so we can create an interface for the Notification class.

export interface Notification {
  type: String,
  message: String,
}

Create a data stream with a service

We can leave our component for now and start building our asynchronous data stream. It is a good idea to have a separate injectable service for that purpose.

Run ‘ng g service notification’ to generate it.

Next, let’s move it to the ‘notification’ directory. A suggested by the Angular team approach for structuring the project is to divide it into modules and features.

Following this style guide, we need to keep everything related to the ‘notification’ feature in the corresponding directory.

Our service would have two simple methods for setting and getting the notification object.

A ‘set’ method will notify all Observers registered to listen for a Subject.

A ‘get’ method will return a Subject as Observable so our component could subscribe to it.

import { Injectable } from '@angular/core';
import { Notification } from './notification';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class NotificationService {
  public subject = new Subject();

  constructor() {}

  set(notification: Notification) {
    this.subject.next(notification);
  }

  get():Observable {
    return this.subject.asObservable();
  }

}

Subscribe to changes

Now we can start working with the notification component.

The primary goal is to subscribe for Subject changes and update the component’s notification object.

Also, we would be able to close the element. Since we render it only when it has an object, we can close it by simply nullifying it.

To do so, we need to create a ‘close’ method to set the notification to null.

An important note! We are not going to inject the service into component via providers, and there is a reason for that.

If you are going to inject it into the component, it will only be working withing its scope.

So, if you will be trying to notify the Observer from the outside of the component, it won’t be updated.

You need to inject a service to the app module instead to make it accessible from anywhere.

I will just import the service and create a new subscription.

import { Component } from '@angular/core';
import { Notification } from './notification';
import { NotificationService } from './notification.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'notification',
  templateUrl: './notification.component.html',
})
export class NotificationComponent {
  public notification: Notification;
  public subscription: Subscription;

  constructor(private notificationService: NotificationService) {
    this.subscription = this.notificationService.get().subscribe(value => {this.notification = value});
  }

  close() {
    this.notificationService.set(null);
  }
}

Code in action

I bet you want to see our component in action! So far, we have only the close method. Let’s create few more.

Include the component into AppComponent. I will also create buttons with two methods: error and success. First one will be sending a message with the type ‘error,’ and the other with the type ‘success.’

<notification></notification>
<button class="Btn Btn--green text--14px text--bold" (click)="success();">success();</button>
<button class="Btn Btn--red text--14px text--bold" (click)="error();">error();</button>

import { Component } from '@angular/core';
import { NotificationService } from './notification/notification.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {

  constructor(private notificationService: NotificationService) {}
  
  success() {
    this.notificationService.set({type: 'success', message: 'Success! Everything went as planned.'});
  }
  error() {
    this.notificationService.set({type: 'error', message: 'Error! Seems that something went wrong.'});
  }
}

You can find the final version of the application here: https://frelseren.github.io/ng-communicate-components/index.html.

And here you can try it out!