Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have defined a number of routes as follows:

export const AppRoutes: Routes = [
  {path: '', component: HomeComponent, data: {titleKey: 'homeTitle'}},
  {path: 'signup', component: SignupComponent, data: {titleKey: 'SIGNUP_FORM.TITLE'}},
  {path: 'signin', component: SigninComponent, data: {titleKey: 'SIGNIN_FORM.TITLE'}},
  {path: 'sendpasswordresetinformation', component: SendPasswordResetInformationComponent},
  {path: 'password/reset/:userAccountToken', component: PasswordResetComponent},
  {
    path: 'dashboard', component: DashboardComponent, children: [
    {path: '', component: DashboardSummaryComponent},
    {
      path: 'message', children: [
      {path: '', component: MessageSummaryComponent, data: {titleKey: 'MESSAGE_LIST.TITLE'}},
      {path: 'conversation/:otherId', component: MessageConversationComponent, data: {titleKey: 'XXX'}}]
    },
    {
      path: 'useraccount', component: UserAccountComponent, children: [
      {
        path: '',
        component: UserAccountSummaryComponent,
        data: {titleKey: 'XXX'},
        resolve: {
          userAccount: UserAccountResolve
        }
      },
      {path: 'address', component: UserAccountAddressComponent, data: {titleKey: 'ADDRESS_FORM.TITLE'}},
      {path: 'email', component: UserAccountEmailComponent, data: {titleKey: 'EMAIL_FORM.TITLE'}},
      {
        path: 'emailnotification',
        component: UserAccountEmailNotificationComponent,
        data: {titleKey: 'EMAIL_NOTIFICATION_FORM.TITLE'}
      },
      {path: 'firstname', component: UserAccountFirstNameComponent, data: {titleKey: 'FIRST_NAME_FORM.TITLE'}},
      {path: 'password', component: UserAccountPasswordComponent, data: {titleKey: 'PASSWORD_FORM.TITLE'}}]
    }]
  }];

Some of the routes are children of others.

I would like to find a way to retrieve the titleKey property on data regardless of whether the route is a top level route or the child of another.

Here is what I have tried:

export class AppComponent implements OnInit {

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

  ngOnInit() {
    this.sessionService.reloadPersonalInfo();
  }

  setTitle($event) { 
    this.translate.get(this.activatedRoute.snapshot.data['titleKey'])
     .subscribe(translation=>this.titleService.setTitle(translation));
  }
}

this.activatedRoute.snapshot.data['titleKey'] is undefined.

Can someone please advise how to retrieve a property on route data regardless of the level of nesting of the route?

edit: After reading the official angular documentation about ActivatedRoute, I tried to use the map operator on the data property of the ActivatedRoute instance as follows:

@Component({
  selector: 'app',
  template: `
              <section class="container-fluid row Conteneur">
                 <appNavbar></appNavbar>
                 <section class="container">
                    <router-outlet (activate)="setTitle($event)"></router-outlet>
                 </section>
              </section>
              <section class="container-fluid">  
                <appFooter></appFooter>
              </section>
              `,
  directives: [NavbarComponent, FooterComponent, SigninComponent, HomeComponent, ROUTER_DIRECTIVES]
})
export class AppComponent implements OnInit {

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

  ngOnInit() {
    this.sessionService.reloadPersonalInfo();
  }

  setTitle($event) {
    this.activatedRoute.data.map(data=>data['titleKey'])
      .do(key=>console.log(key))
      .switchMap(key=>this.translate.get(key))
      .subscribe(translation=>this.titleService.setTitle(translation));
  }
}

and yet key is always undefined...

like image 499
balteo Avatar asked Aug 10 '16 15:08

balteo


People also ask

What does the ActivatedRoute interface allows you to access?

ActivatedRoutelink. Provides access to information about a route associated with a component that is loaded in an outlet. Use to traverse the RouterState tree and extract information from nodes.

What is router and ActivatedRoute?

RouterState and ActivatedRoute are similar to their snapshot counterparts except that they expose all the values as observables, which are great for dealing with values changing over time. Any component instantiated by the router can inject its ActivatedRoute.

How do I use ActivatedRoute?

Step 1: Import ActivatedRoute from Router module. import { ActivatedRoute } from '@angular/router'; Step 2: Inject ActivatedRoute in constructor. Now we have id in edit_quiz.


2 Answers

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.

like image 149
Robba Avatar answered Sep 23 '22 16:09

Robba


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));
  }
}
like image 37
balteo Avatar answered Sep 26 '22 16:09

balteo