Retrieving a data property on an Angular2 route regardless of the level of route nesting using ActivatedRoute

We had this same problem. We decided to go about it another way. Instead of trying to listen for the data emits on the ActivatedRoute, we subscribe to the events Observable on the router itself:

import { Component } from "@angular/core";
import { NavigationEnd, Router } from "@angular/router";

declare var module: any;

@Component({
    moduleId: module.id,
    selector: "app-layout",
    templateUrl: "main-layout.component.html"
})
export class LayoutComponent {
    titleKey: string;

    constructor(private router: Router){}

    ngOnInit() {
        this.router.events
            .filter((event: any) => event instanceof NavigationEnd)
            .subscribe(() => {
                var root = this.router.routerState.snapshot.root;
                while (root) {
                    if (root.children && root.children.length) {
                        root = root.children[0];
                    } else if (root.data && root.data["titleKey"]) {
                        this.titleKey = root.data["titleKey"];
                        return;
                    } else {
                        return;
                    }
                }
            });
    }
}

Note that we're using the value in a component that is used at the top level but needs the data from the deepest child route. You should be able to pretty easily convert this to a service that emits events whenever the titleKey changes value.

Hope this helps.


I stumbled on a nice tutorial with a clean solution: https://toddmotto.com/dynamic-page-titles-angular-2-router-events.

Here is what I finally came up with using the solution from the above tutorial and using the ng2 translate service in order to convert the titleKeys specified in my route data into proper labels:

@Component({
  selector: 'app',
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['../styles/bootstrap.scss'],
  template: `<section class="container-fluid row Conteneur">
                 <app-navbar></app-navbar>
                 <section class="container">
                    <router-outlet></router-outlet>
                 </section>
              </section>
              <section class="container-fluid">  
                <app-footer></app-footer>
              </section>`
})
export class AppComponent implements OnInit {

  constructor(private translate: TranslateService,
              private sessionSigninService: SessionSigninService,
              private titleService: Title,
              private router: Router,
              private activatedRoute: ActivatedRoute) {
    let userLang = 'fr';
    translate.use(userLang);
    moment.locale(userLang);
  }

  ngOnInit() {

    this.router.events
      .filter(event => event instanceof NavigationEnd)
      .map(() => this.activatedRoute)
      .map(route => {
        while (route.firstChild) route = route.firstChild;
        return route;
      })
      .filter(route => route.outlet === 'primary')
      .mergeMap(route => route.data)
      .mergeMap(data => this.translate.get(data['titleKey']))
      .subscribe(translation => this.titleService.setTitle(translation));
  }
}