1

Slowly getting to grips with angular 2 but I've come to dead end on one particular issue.

If you have this arrangement;

<parent>
    <child/>
</parent>

I can notify the child of changes in the parent by using @Input() and get it to listen to changes. I can notify the parent of changes in the child by using the @Output() and EventEmitters.

What I'm now looking at is notifying a routed component (a component that is loaded dynamically when a link it clicked) about a specific event that happens in the main appComponent. See example below;

<app>
    <button (click)='addEntity()>Add</button>
    <router-outlet></router-outlet>
</app>

I want to be able to notify the component that is loaded into the router-outlet that the button has been clicked.

An example of usage would be having multiple features (heroes/contacts/pets etc) each with its own dialog for adding an entity. Depending on the selected route, it would need to notify the selected feature component (e.g. ContactComponent) to display its specific detail component/view/template (e.g. contact-detail.component).

Whats the best way to achieve this?

Cheers

Mark
  • 170
  • 4
  • 13

2 Answers2

6

There's also the possibility of just using a shared service to communicate between different components, e.g. via a messenger service like this:

MessengerService

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

@Injectable()
export class MessengerService {
    private messageSource: BehaviorSubject<boolean> = new BehaviorSubject(true); 
    public message = this.messageSource.asObservable();
    public buttonClicked(value: boolean) {
        this.messageSource.next(value);
    }
}

Parent Component

export class ParentComponent {
    constructor(private messengerService: MessengerService) { }
    addEntity() {
        this.messengerService.buttonClicked(true);
    }
}

Child Component

import { Subscription } from 'rxjs/Rx';
export class ChildComponent implements OnInit, OnDestroy {
    private messageSubscription: Subscription;
    constructor(private messengerService: MessengerService) { }
    ngOnInit() {
        this.messageSubscription = this.messengerService.message.subscribe(m => {
            // Act upon the click event
        });
    }
    ngOnDestroy() {
        this.messageSubscription.unsubscribe();
    }
}

This would be a clean way to decouple the components and rely on a messenger service (that you could subscribe to in multiple components, wherever it is required)

GeorgDangl
  • 2,146
  • 1
  • 29
  • 37
  • It does appear that a shared service is the only real way to achieve this. I have read somewhere that services shouldn't use event emitters which for me would be the best option because I am not communicating a state but an intent i.e. addEntity is saying that the user wants to see the addEntity screen, it does not handle the visibility of that screen (because it may already be visible). Either way this does work, just seems odd as it would appear that you are subscribing to an observable whose value never really changes. – Mark Feb 24 '17 at 09:40
  • This solved an edge case problem for me that I was really unsure how to solve in a tidy way otherwise. Thanks. – trees_are_great Mar 16 '17 at 14:54
0

Well if I understand well your question try this: put a return value on the addEntity() method, can be an string, or something that you know the kind of thing that you are adding (heroes/contacts/pets). Then on the main template put a local variable that takes the return of the addEntity() method like this:

<button (click)='#type_added = addEntity()>Add</button>

then you can pass that "#type_added" local variable to the other component like this:

<router-outlet [added] = #type_added></router-outlet>

and you must declare in your router-outlet component (.ts) the variable "added" whidth the @Output.

Adrian
  • 171
  • 1
  • 1
  • 6
  • Are you sure this is working? `router-outlet` is the [selector of the component](https://angular.io/docs/ts/latest/api/router/index/RouterOutlet-directive.html) into which child routes components are rendered, not a custom component. – GeorgDangl Feb 23 '17 at 19:01