Francesco Borzi
Francesco Borzi

Reputation: 61724

Angular ActivatedRoute data returns an empty object

I have a route registered with some data:

const routes: Routes = 
[
    {path: 'my-route', data: { title: 'MyTitle' }, component: MyComponent},
];

and I'm trying to access to the route's data using ActivatedRoute:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({...})
export class MyComponent implements OnInit {
  private routeData;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.routeData = this.route.data.subscribe((data) => {
      console.log(data); // this is returning an empty object {}
    });
  }
}

but for some reasons data is an empty object.

How to solve this problem?

Upvotes: 102

Views: 60322

Answers (8)

Christopher Peisert
Christopher Peisert

Reputation: 24104

2024 Update: Get route custom data for component outside <router-outlet> (Angular 17+)

This solution is inspired by Brandon Roberts' solution and updated for Angular 17 (Signals) and RxJS 7.

app.component.ts

import { CommonModule } from '@angular/common'
import { Component, DestroyRef, OnInit, inject, signal } from '@angular/core'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { ActivatedRoute, NavigationEnd, Router, RouterModule } from '@angular/router'
import { switchMap } from 'rxjs'
import { filter } from 'rxjs/operators'
import { LeftNavComponent } from './core/left-nav/left-nav.component'
import { RouteData } from './route-data'

@Component({
  standalone: true,
  imports: [LeftNavComponent, RouterModule, CommonModule],
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  hideNav = signal(false)
  private readonly destroyRef = inject(DestroyRef)

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

  ngOnInit(): void {
    this.router.events.pipe(
      takeUntilDestroyed(this.destroyRef),
      filter(e => e instanceof NavigationEnd),
      switchMap(() => this.activatedRoute.firstChild ? this.activatedRoute.firstChild.data : this.activatedRoute.data),
    ).subscribe((data: RouteData) => {
      this.hideNav.set(data.hideNav || false)
    })
  }
}

app.component.html

<div class="app-container">

  <main class="main-content">
    <router-outlet></router-outlet>
  </main>

  @if (!hideNav()) {
    <right-nav class="right-nav"></right-nav>
  }

</div>

route-data.ts

Helper interface for typed data.

export interface RouteData {
  hideNav?: boolean
}

Upvotes: 6

xianshenglu
xianshenglu

Reputation: 5309

Answer from Angular contributor, link

this.router.events.pipe(
    filter((e) => e instanceof NavigationEnd),
    map(() => {
        let route = this.activatedRoute;

        while (route.firstChild) {
            route = route.firstChild;
        }

        return route;
    }),
    filter((route) => route.outlet === 'primary'),
    mergeMap((route) => route.data),
    tap(console.log)
);

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

Upvotes: 2

Oleg Averkov
Oleg Averkov

Reputation: 342

Get route custom data for component outside <router-outlet> (Angular 8):

constructor(private router: Router) {}
    
ngOnInit() {
    this.router.events.subscribe(data => {
      if (data instanceof ActivationStart) {
        console.log(`Custom data`, data.snapshot.data);
      }
    });
  }

Upvotes: 12

Bluerain
Bluerain

Reputation: 506

import { AfterViewInit, Component} from '@angular/core';
import { ActivatedRoute } from '@angular/router';


@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.scss']
})
export class UsersComponent implements AfterViewInit {
  paramId: string;

  constructor(private activatedRoute: ActivatedRoute) {}

  ngAfterViewInit(): void {
    this.paramId = this.activatedRoute.snapshot.params.id;
    console.log('paramId =', this.paramId);
  }

}

Upvotes: -2

Josip
Josip

Reputation: 75

This is part of my code (NOTE: I'm using Angular 8):

constructor(private _router: Router, private _route: ActivatedRoute) {}

ngOnInit() {
  ...
  this.listenRouting();
}

listenRouting() {
    this._router.events.subscribe((router: any) => {
      routerUrl = router.url;
      if (routerUrl && typeof routerUrl === 'string') {
        routerList = routerUrl.slice(1).split('/');
        console.log("data.breadcrumbs", this._route.snapshot.routeConfig.children.find(child => child.path === routerList[routerList.length - 1]).data.breadcrumbs);
        ...
      }
    });
}

So data is "hiding" under ActivatedRoute.snapshot.routeConfig.children Array. Each child contains, among other things, data. In my case data is configured in routing.module, e.g.:

const routes: Routes = [
  { path: "facility-datas",
    component: FacilityTerminal,
    data: {
      breadcrumbs: "b ft"
    }
  },
...
];

Upvotes: 2

Blazzze IL
Blazzze IL

Reputation: 99

I Don't know the version spoken, but as of Angular 6, this worked for me:

(Ofcourse thanks to shinDath)

  routeData;
  ngOnInit() {
    //child route param doesnt go up to parent route params.
    this.router.events.subscribe((val) => {
      if (val instanceof ActivationEnd) {
        if(!$.isEmptyObject(val.snapshot.params)){
          this.routeData = val.snapshot.params;
        }
      }
    });
  }

Upvotes: 4

Francesco Borzi
Francesco Borzi

Reputation: 61724

Edit: the problem is that I was trying to access the ActivatedRoute from a Component which is outside the <router-outlet>. So it looks like that this is the intended behaviour.

However I still think that my answer below can be useful to anyone who is trying to accomplish the same thing.


I found a workaround on GitHub (thanks manklu) that I used in order to accomplish what I needed:

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

@Component({...})
export class MyComponent implements OnInit {
  private routeData;

  constructor(private router: Router) { }

  ngOnInit() {
    this.router.events.subscribe((data) => {
      if (data instanceof RoutesRecognized) {
        this.routeData = data.state.root.firstChild.data;
      }
    });
  }
}

doing this way this.routeData will hold the route data that I needed (in my case the page title).

Upvotes: 143

Rohit Ramname
Rohit Ramname

Reputation: 834

Below should work:

constructor(private route: ActivatedRoute) {}

ngOnInit() {
    console.log(this.route.snapshot.data);
}

Upvotes: -9

Related Questions