Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to communicate between two components with no parent-child relationship?

The app.component.ts is the entry point of my Angular 2 app. It holds two components: a header and a sidenav.

@Component({
  selector: 'app-root',
  template: `
    <app-header></app-header>

    <app-sidenav></app-sidenav>
  `
})
export class AppComponent { }

Here's what I want to achieve: Inside the <app-header> component I have a (menu) button, that should trigger the opening of the <app-sidenav> component.

In other words, a click event on the button in the <app-header> should trigger the open() method inside the <app-sidenav> component.

  • header.component.ts:

    @Component({
      selector: 'app-header',
      template: `
        <button (click)="onSidenavOpen()">Menu</button>
      `
    })
    export class HeaderComponent {
      constructor() { }
    
      onSidenavOpen() {
        // trigger the opening of the <app-sidenav> component
      }
    }
    
  • sidebar.component.ts:

    @Component({
      selector: 'app-sidenav',
      template: `
        <md-sidenav #sidenav>
          // bla bla, sidebar
        </md-sidenav>
      `
    })
    export class SidenavComponent {
      constructor() { }
    
      open(sidenav) {
        sidenav.open();
      }
      close(sidenav) {
        sidenav.close();
      }
    }
    

I read the component interaction section in the Angular 2 docs, but I am struggling to find the best approach to communicate between these two components. The general intention I have is to encapsulate as much logic as possible for each component.

How would you approach my use-case? Would you create an intermediate service? Please share an example.

like image 636
Kaloyan Kosev Avatar asked Feb 16 '17 16:02

Kaloyan Kosev


1 Answers

You can use a service to allow two separate components to talk to each other. In the example below, the HeaderComponent pushes a value to an exposed Observable with the toggleSideNav method of the SidebarService. The SidenavComponent subscribes to this Observable on its initial load and will respond to a new value being pushed through.

SidebarService:

import { Injectable } from "@angular/core"

import { Observable, Subject } from "rxjs/Rx";

@Injectable()
export class SidebarService {

    private sidenavOpenSubject : Subject<boolean>;

    constructor() {
        this.sidenavOpenSubject = new Subject<boolean>();
    }

    toggleSideNav(opening: boolean): void {
        this.sidenavOpenSubject.next(opening);
    }

    onSideNavToggle(): Observable<boolean> {
        return this.sidenavOpenSubject;
    }

}

HeaderComponent:

@Component({
  selector: 'app-header',
  template: `
    <button (click)="onSidenavOpen()">Menu</button>
  `
})
export class HeaderComponent {
  constructor(private sidebarService: SidebarService) { }

  onSidenavOpen() {
    this.sidebarService.toggleSideNav(true);
  }
}

SidebarComponent:

@Component({
  selector: 'app-sidenav',
  template: `
    <md-sidenav #sidenav>
      // bla bla, sidebar
    </md-sidenav>
  `
})
export class SidenavComponent implements OnInit {
    constructor(private sidebarService: SidebarService) { }

    ngOnInit(): void {
        this.sidebarService.onSideNavToggle().subscribe(
            (opening) => {
                if (opening) {
                    //Logic to open the sidenav here
                } else {
                    //Logic to close the sidenav here
                }
            }
        );
    }

    open(sidenav) {
        sidenav.open();
    }

    close(sidenav) {
        sidenav.close();
    }
}

The SidebarService needs to be registered in a suitable module (e.g. your AppModule).

like image 81
Seaal Avatar answered Oct 19 '22 05:10

Seaal