Reputation: 4538
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
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
Reputation: 11934
Based on @yurzui's idea, you can use a directive for this:
@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();
}
}
@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);
})
}
}
Upvotes: 3
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