Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 output from router-outlet

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!

like image 951
Velidan Avatar asked Jun 06 '16 16:06

Velidan


People also ask

How do you emit an event from router outlet?

You can define an @Output event emitter in the component being rendered at router-outlet. define router outlet as follows. In your component you can do as so. This way you can send the events to the component which is rendering router-outlet.

What does router outlet do in Angular?

The Router-Outlet is a directive that's available from the router library where the Router inserts the component that gets matched based on the current browser's URL. You can add multiple outlets in your Angular application which enables you to implement advanced routing scenarios.


4 Answers

<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 :)

like image 86
Antara Datta Avatar answered Oct 06 '22 09:10

Antara Datta


<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.

like image 31
Günter Zöchbauer Avatar answered Oct 06 '22 09:10

Günter Zöchbauer


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.

like image 6
Bacem Avatar answered Oct 06 '22 09:10

Bacem


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});
  }
like image 2
Raza Avatar answered Oct 06 '22 10:10

Raza