AlexB
AlexB

Reputation: 4538

Angular - get component instance in router events

I'm trying to implement a title service for my angular 10 app. I need to subscribe to router events, grab the activated route's component, see if it implements title() getter and then use it to set the page's title. Sounds easy...

The code:

    this.router.events
      .pipe(
        filter((event) => event instanceof NavigationEnd),
        map(() => this.rootRoute(this.route)),
        filter((route: ActivatedRoute) => route.outlet === "primary"),
        filter(
          (route: ActivatedRoute) =>
            ComponentWithTitleBase.isPrototypeOf(route.component as any)
        ),
        map((route) => (route.component as unknown) as ComponentWithTitleBase),
        tap(console.dir)
      )
      .subscribe((comp: ComponentWithTitleBase) => {
        this.titleSvc.title = comp.title;
      });

But the comp.title is ALWAYS undefined. Even though the component does implement get title() getter:

export class AboutComponent extends ComponentWithTitleBase implements OnInit {
  get title(): string {
    return "About the demo";
  }

  ...
}

I see that console.dir outputs AboutComponent. What am I missing here?

Upvotes: 4

Views: 3080

Answers (3)

Shy Agam
Shy Agam

Reputation: 1415

With the @bespunky/angular-zen library you can do this:

In the template holding the router outlet:

<router-outlet publishComponent></router-outlet>

Then, in the component that needs access to the instance:

import { Component     } from '@angular/core';
import { NavigationEnd } from '@angular/router';
import { RouteAware    } from '@bespunky/angular-zen/router-x';

@Component({
    selector   : 'app-demo',
    templateUrl: './demo.component.html',
    styleUrls  : ['./demo.component.css']
})
export class DemoComponent extends RouteAware
{
    protected onNavigationEnd(event: NavigationEnd): void
    {
        const currentInstance = this.componentBus.instance();

        console.log(`Navigation ended. Current component instance:`, currentInstance )
    }
}

It is open-source and you can install the library like this:

npm install @bespunky/angular-zen

Here's a live example with more details.

💡 If your router outlet has a name, you can pass it to the instance() method and retrieve the corresponding outlet's component.

Upvotes: 0

Andrei Gătej
Andrei Gătej

Reputation: 11934

Based on @yurzui's idea, you can use a directive for this:

activated-component.directive.ts

@Directive({
  selector: 'router-outlet'
})
export class ActivatedComponentsDirective {

  constructor(r: RouterOutlet, titleService: TitleService) {
    r.activateEvents.pipe(
      // takeUntil(r.destroyed),
    ).subscribe(compInstance => compInstance.title && titleService.newTitle(compInstance.title))
  }

  ngOnDestroy () {
    // destroyed.next;
    // destroyed.complete();
  }
}

title.service.ts

@Injectable({
  providedIn: 'root'
})
export class TitleService {

  private src = new Subject<string>();

  newTitle (t: string) {
    this.src.next(t);
  }

  constructor() { this.initConsumer() }

  private initConsumer () {
    this.src.pipe(
      /* ... */
    ).subscribe(title => {
      console.log('new title', title);
    })
  }
}

ng-run demo.

Upvotes: 3

Andrei
Andrei

Reputation: 12036

there is a little misunderstanding. when you console.dir the .component you get not an instance of AboutComponent but a class of it. thus your getter should be static if you want to access it as component.title

static get title(): string {
  return "About the demo";
}

Upvotes: 1

Related Questions