Angular 2 output from router-outlet

BindingAngular

Binding Problem Overview


I want to make navigation from child components that render inside router-outlet. My parent component have a router config and I want to navigate manually on some event. But I don't know how I can pass from child to parent some data (for navigation) without output. Because this construction is non working

 <router-outlet (navigateTo)="navigateToMessagePart($event)"></router-outlet>

How I can do it in right way? Maybe navigate it from child? But how I can get parent methods from child. Many thanks for any help!

Binding Solutions


Solution 1 - Binding

<router-outlet></router-outlet> can't be used to emit an event from the child component. One way to communicate between two components is to use a common service.

Create a service

> shared-service.ts

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

@Injectable()
export class SharedService {
	// Observable string sources
	private emitChangeSource = new Subject<any>();
	// Observable string streams
	changeEmitted$ = this.emitChangeSource.asObservable();
	// Service message commands
	emitChange(change: any) {
		this.emitChangeSource.next(change);
	}
}

Now inject the instance of the above service in the constructor of both the parent and child component.

The child component will be emitting a change every time the onClick() method is called

> child.component.ts

import { Component } from "@angular/core";

@Component({
	templateUrl: "child.html",
	styleUrls: ["child.scss"]
})
export class ChildComponent {
	constructor(private _sharedService: SharedService) {}

	onClick() {
		this._sharedService.emitChange("Data from child");
	}
}

The parent component shall receive that change. To do so,capture the subscription inside the parent's constructor.

> parent.component.ts

import { Component } from "@angular/core";

@Component({
	templateUrl: "parent.html",
	styleUrls: ["parent.scss"]
})
export class ParentComponent {
	constructor(private _sharedService: SharedService) {
		_sharedService.changeEmitted$.subscribe(text => {
			console.log(text);
		});
	}
}

Hope this helps :)

Solution 2 - Binding

<router-outlet></router-outlet> is just a placeholder for adding routed components. There is no support for any kind of binding.

You can create a custom <router-outlet> that allows you to do that or more common, use a shared service to communicate between parent component and routed component.

For more details see https://angular.io/docs/ts/latest/cookbook/component-communication.html

update

There is now an event that allows to get the added component

<router-outlet (activate)="componentAdded($event)" (deactivate)="componentRemoved($event)"></router-outlet>

which allows to communicate (call getters, setters, and methods) with the component in componentAdded()

A shared service is the preferred way though.

Solution 3 - Binding

The answer given above is correct and complete. I just want to add for those who the solution didn't work for them that they should add the service to providers only in the parent component and not the child to ensure that you get a singleton of the service, otherwise two service instances will be created. This response is inspired by the comment of @HeisenBerg in the previous response.

Solution 4 - Binding

I changed a little from Antara Datta's answer. I created a Subscriber service

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

@Injectable({
  providedIn: 'root'
})
export class Subscriber<T>
{
  protected observable = new Subject<T>();

  public next(item: T)
  {
    this.observable.next(item);
  }

  public subscribe(callback: (item:T)=>void) {
    this.observable.subscribe(callback);
  }
}

Whenever I need two components to share some information, I inject this service in the constructor which subscribe to it:

 constructor(protected layoutOptions: Subscriber<Partial<LayoutOptions>>)
 {
    layoutOptions.subscribe(options => this.options = Object.assign({}, this.options, options));
 }

and the one which updates it

constructor(protected router: Router, protected apiService: ApiService, protected layoutOptions: Subscriber<Partial<LayoutOptions>>)
  {
    this.layoutOptions.next({showNavBar: false});
  }

Solution 5 - Binding

It escapes my understanding why the router does not forward the "@Outputs".

I ended up dispatching barebones DOM events

// dom node needs to be a reference to a DOM node in your component instance
// preferably the root
dom.dispatchEvent(
      new CustomEvent('event', {
        detail: payload, // <- your payload here
        bubbles: true,
        composed: true,
      })
    );

You can catch it anywhere up the DOM tree like any other DOM event

Note: you need to unpack the payload from { detail: payload } on the receiving end..

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVelidanView Question on Stackoverflow
Solution 1 - BindingAntara DattaView Answer on Stackoverflow
Solution 2 - BindingGünter ZöchbauerView Answer on Stackoverflow
Solution 3 - BindingBacemView Answer on Stackoverflow
Solution 4 - BindingRazaView Answer on Stackoverflow
Solution 5 - BindingJan KretschmerView Answer on Stackoverflow