Amici Nybråten
Amici Nybråten

Reputation: 57

Angular 6 - Unable to retrieve static data

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>

Upvotes: 3

Views: 2603

Answers (4)

conceptdeluxe
conceptdeluxe

Reputation: 3893

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.

Upvotes: 1

netlander
netlander

Reputation: 1186

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.

Upvotes: 0

Abhinav
Abhinav

Reputation: 2085

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.

Upvotes: 1

Abinesh Devadas
Abinesh Devadas

Reputation: 1547

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));
  ...
}
...

Upvotes: 5

Related Questions