Angular's Getting Started Guide contains recipes for common component communication scenarios in which two or more components share information.

The scenarios discussed, include:

  • Pass data from parent to child with input binding
  • Intercept input property changes with a setter
  • Intercept input property changes with ngOnChanges()
  • Parent listens for child event
  • Parent interacts with child via local variable
  • Parent calls an @ViewChild()
  • Parent and children communicate via a service

Each scenario discusses parent -> child or child -> parent interaction.

In this post, I'll walk you through the steps I followed to enable sibling component interaction.

Excel-like UI

In the previous post, I wrote about the steps I followed when creating an application with an Excel-like UI.

The following regions compose Excel's UI:

  • Quick Access Toolbar
  • Ribbon (including Ribbon Tabs)
  • Formula Bar
  • Worksheet Grid
  • Worksheet Tabs
  • Status Bar

At this point, the application has a Ribbon component, a Formula Bar and a Worksheet component:


The Application Component

Let's take a look at the template for the AppComponent (app.component.html):

<div class="excelbook" style="position: relative; overflow-y: hidden;" (window:resize)="onResize($event)">

  <div id="ribbon-tabs-container">
    <app-ribbon (ribbonClicked)="ribbonClicked($event)"></app-ribbon>

  <div id="worksheet-container" [style.height.px]="worksheetHeight">


The AppComponent (the parent) has two child components: the RibbonComponent (<app-ribbon>); and the WorksheetComponent (<app-worksheet>). The RibbonComponent and the WorksheetComponent are siblings:


The Ribbon Component

Let's take a look at the template for the RibbonComponent (ribbon.component.html):

<div class="row ribbon-container">

  <!-- File Panel -->
  <div class="panel panel-default">
    <div class="panel-body">
      <div class="btn-group">


        <div class="btn btn-default btn-large no-border">
          <span class="glyphicon glyphicon-open"></span>
          <span class="text">Load</span>
          <input type="file" class="load"
          (change)="command({ methodName: 'fileLoad', param1: $event })"
          application/" />
    <div class="panel-footer text-center">File</div>

When a user click's the 'Load' button the RibbonComponent's command() method is invoked:

export class RibbonComponent {

  @Output() ribbonClicked = new EventEmitter<any>();

  command(action: any) {

And the RibbonComponent emits a ribbonClicked event that the parent (the AppComponent) binds to:

  <div id="ribbon-tabs-container">
    <app-ribbon (ribbonClicked)="ribbonClicked($event)"></app-ribbon>

The parent (the AppComponent) inject's a child component (the WorksheetComponent) as a @ViewChild so that it can react to the ribbonClicked event:

export class AppComponent implements OnInit {

  private worksheet: WorksheetComponent;
  ribbonClicked(event: any) {

    const methodName = event.methodName;

    if (this.worksheet[methodName]) {
      if (event.hasOwnProperty('param1')) {
        const param1 = event.param1;
      } else {

By invoking a WorksheetComponent method, for example, fileLoad():

  fileLoad(event: any) {
    if (this.flexSheet &&[0]) {
      this.flexSheet.loadAsync([0]); = '';

You should see output like:


In the scenario discussed above, we have enabled sibling component interaction (albeit indirectly) by utilising the Parent listens for child event and the Parent calls an @ViewChild() component communication scenarios.

Service, Observable and a Subject

Another approach to sibling component interaction is to use a Service, an Observable and a Subject.

The Worksheet Service

Let's take a look at the Worksheet Service (worksheet.service.ts):

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

export class WorksheetService {

  private selectionFormatState = new Subject<any>();

  setState(state: any) {;

  getState(): Observable<any> {
    return this.selectionFormatState.asObservable();

The Worksheet Component

The WorksheetComponent can use the WorksheetService's setState() method to share state information:

import * as wjcGridSheet from 'wijmo/wijmo.grid.sheet';
import { WorksheetService } from '../services/worksheet.service';

export class WorksheetComponent {

  private flexSheet: wjcGridSheet.FlexSheet;
  selectionFormatState: wjcGridSheet.IFormatState = {};

  constructor(private worksheetService: WorksheetService) {
  applyBoldStyle() {
    if (this.flexSheet) {
      this.flexSheet.applyCellsStyle({ fontWeight: this.selectionFormatState.isBold ? 'none' : 'bold' });
      this.selectionFormatState.isBold = !this.selectionFormatState.isBold;

The Ribbon Component

The RibbonComponent can use the WorksheetService's getState() method to obtain (WorksheetComponent) state information:

import { WorksheetService } from '../services/worksheet.service';
import { Subscription } from 'rxjs/Subscription';

export class RibbonComponent implements OnDestroy {

  private subscription: Subscription;
  selectionFormatState: any = {};

  constructor(private worksheetService: WorksheetService) {
    this.subscription = this.worksheetService.getState().subscribe(
      selectionFormatState => {
        this.selectionFormatState = selectionFormatState;
  ngOnDestroy() {

Now we can use the selectionFormatState to toggle the Ribbon buttons active state:

  <!-- Font Panel -->
  <div class="panel panel-default">
    <div class="panel-body">
      <div class="btn-group-vertical">
        <div class="btn-group btn-group-h">
          <button type="button" class="btn btn-default btn-small {{selectionFormatState.isBold ? 'active' : ''}}"
                  title="Bold" (click)="command({ methodName: 'applyBoldStyle' })">
            <span class="glyphicon glyphicon-bold"></span>
            <span class="text">Bold</span>
    <div class="panel-footer text-center">Font</div>

The Ribbon's buttons reflect the WorksheetComponent's selectionFormatState:


Source Code:
Additional Resources: