3

Problem: static data defined at route is never retrieved with subscription to data object at ActivatedRoute. Everything else seems to work fine, the data object is not null, but I can't get data from it. When I try to debug the data from the data object it outputs "undefined", when I try to bind it to UI nothing shows up, but when I look at ActivatedRoute messages in Chrome it has the data. After many tries I'm pretty sure my syntax should work based on many examples, so has something changed in Angular 6 perhaps, or something wrong with Angular?

Route code:

    const appRoutes: Routes = [
  {
    path: "article",
    redirectTo: "/article/partners",
    pathMatch: "full"
  },
  { 
    path: "article",
    children: [
      {
        path: "bawo",
        component: BawoArticleComponent,
        data: { title: 'BaWo' }
      },
      {
        path: "goldenhands",
        component: GoldenHandsArticleComponent,
        data: { title: 'Golden Hands' }
      },
      {
        path: "investors",
        component: InvestorsArticleComponent,
        data: { title: 'Investors' }
      },
      {
        path: "partners",
        component: PartnersArticleComponent,
        data: { title: 'Partners' }
      }
    ]
  },
  {
    path: "**",
    redirectTo: "/article/partners"
  }
];

Retrieval component code (I've commented where the relevant code is):

export class ArticleSelectorComponent implements OnInit {
  arrowFader: string;

  opacity: string;

  fadeTimer: Observable<number>;

  constructor(private router: Router, private activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.router.events.subscribe((e: RouterEvent) => {
      this.fadeTimer = timer(0, 150);
      let subscription = this.fadeTimer.subscribe(currentValue => {

        let calc = currentValue & 3;

        if (calc == 0) {
          this.arrowFader = '>';
          this.opacity = '0.5';
        }
        else if (calc == 1) {
          this.arrowFader = '>>';
          this.opacity = '0.65';
        }
        else {
          this.arrowFader = '>>>';
          this.opacity = '0.8';
        }
      });

      this.fadeTimer.subscribe(currentValue => {
        if(currentValue >= 14) {
          subscription.unsubscribe();
          this.opacity = '1.0';
        }
      });
    });

// THIS DOESN'T WORK!!!!
    this.activatedRoute.data.subscribe((data: Data) => {
      console.log(data['title']);
    });
  }

// not relevant, this code is ran with parameter at html buttons
  navToArticle(num: number) {
    let navStr = '';
    switch(num){
      case 1: {
        navStr = '/article/bawo';
        break;
      }
      case 2: {
        navStr = '/article/goldenhands';
        break;
      }
      case 3: {
        navStr = '/article/partners';
        break;
      }
      case 4: {
        navStr = '/article/investors';
        break;
      }
    }

    this.router.navigateByUrl(navStr);
  }
}

HTML code for AppComponent (with component directives):

<div class="site">

    <div class="top">
        <div class="anim-in-left">
            <app-domains></app-domains>
        </div>

        <div class="anim-in-down top-title">
            <h1 class="top-title-text">{{ topTitle }}</h1>
        </div>

        <div class="anim-in-right">
            <app-presence></app-presence>
        </div>
    </div>

    <div class="anim-in-up middle">
        <app-article-selector></app-article-selector>
    </div>
</div>

4 Answers4

5

Try Below Snippet, Because if you subscribe to activatedRoute immediately it subscribes only to the router data changes where the current component is registered in router configuration and I just added NavigationEnd filter so it wont be triggered for all other events which is not needed for this requirement.

...    
ngOnInit() {
  ...
  this.title$ = this.router.events.pipe(
    filter((event) => event instanceof NavigationEnd),
    map(_ => this.activatedRoute),
    map((route) => {
      while (route.firstChild) {
        route = route.firstChild;
      }

      return route;
    }),
    mergeMap((route) => route.data),
    map((data) => data.title)
  );
  this.title$.subscribe(title => console.log(title));
  ...
}
...
Abinesh Devadas
  • 1,517
  • 13
  • 15
  • thnx lots and lots! – Amici Nybråten Jun 18 '18 at 21:43
  • This code returns an empty array after it worked great in the past. I think it's breaking since 6.0 upgrade. – netlander Jun 19 '18 at 18:45
  • @netlander No i just now checked it does works in 6.0 as well, do you have any stackblitz link i can take a look into your code ? – Abinesh Devadas Jun 19 '18 at 19:50
  • @AbineshDevadas my project is based on Universal Starter (https://github.com/angular/universal-starter) and wouldn't run on stackblitz (https://stackblitz.com/github/angular/universal-starter). The code I'm using (see my answer below) used to work until recently, not sure why it stopped. – netlander Jun 20 '18 at 09:06
  • @netlander I had created with your code it does work please check the link https://stackblitz.com/edit/dynamic-routes?embed=1&file=app/app.component.ts&view=preview – Abinesh Devadas Jun 20 '18 at 10:29
1

I forked the Angular example and duplicated your code (to an extent) at ->

The only difference I found was how the component was being activated.

  1. ArticleSelectorComponent is never part of routing lifecycle when just imported as as an object.
  2. The moment it is made part of routing lifecycle (as a routing component) it works like a charm :D

I haven't tried @abinesh-devadas response but that looks like a better solution, if you really want to obtain the data element irrespective of component lifecycle.

Abhinav
  • 2,085
  • 1
  • 18
  • 31
1

Fortunately, the question has been answered already:

https://stackoverflow.com/a/46305085/1510754

https://github.com/angular/angular/issues/11812#issuecomment-248820529

Based on this here is a more complete answer:

import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router, RoutesRecognized } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})

export class AppComponent implements OnInit {

  private baseTitle = 'MySite';

  get title(): string {
    return this.titleService.getTitle();
  }

  constructor(
    private router: Router,
    private titleService: Title,
  ) {
  }

  ngOnInit() {
    this.router.events.subscribe(event => {
      if (event instanceof RoutesRecognized) {
        const route = event.state.root.firstChild;
        let title = this.baseTitle;
        if (route.data['title']) {
          title = route.data['title'] + ' - ' + title;
        }
        this.titleService.setTitle(title);
      }
    });
  }

}

Note: the title getter is not needed for setting the <title> as this is done with the titleService. But you can use the getter for updating a <h1> for instance.

conceptdeluxe
  • 3,753
  • 3
  • 25
  • 29
0

The code below worked fine until recently:

this.router.events
    .pipe(
        filter((event: any) => 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)
    )
    .subscribe((event) => {
            this.titleService.setTitle(event['title']);
            console.log(event);
        }

Since the upgrade to Angular 6.0 I think. No matter what I try I get undefined.

netlander
  • 1,128
  • 1
  • 11
  • 12