6

I would like to use Semantic UI in my Angular2 application. The problem is that I can't find a router setting that changes the default name of "router-link-active" class. I need it to be called just "active" to make menus display properly.

As I understand, such setting doesn't exist. I've seen it in Vue.JS so I expect it to be there too. Is it a good idea to ask developers to fix this?

So. We need to write a custom directive that adds "active" class to all DOM elements with "router-link-active" class, but I've got some problems here too.

There is a similar question but the answer is too complicated and didn't work for me. So I have read some documentation and decided to do something better like this:

commons.ts:

@Directive({
    selector: '.router-link-active',
    host: {'[class.active]': 'trueConst'} //just 'true' could also work I think...
})
export class ActiveRouterLinkClass {
    trueConst: boolean = true; //...if 'true' works we don't need this
}

Then I've imported ActiveRouterLinkClass into my main.component.ts and added it to the component's directives list. Unfortunately, now I have this error: "EXCEPTION: Unexpected directive value 'undefined' on the View of component 'Main'". Please, explain what I did wrong!

Community
  • 1
  • 1
N. Kudryavtsev
  • 3,556
  • 1
  • 26
  • 30

2 Answers2

4

Angular doesn't apply directives or components to selectors that are dynamically applied. If the class .router-link-active is added to a link is dynamic and therefore won't work.

What you can do instead is using a more generic selector like [routerLink] and than read whether .router-link-active is set using an @Input() and set the desired class using host binding.

@Directive({
  selector: '[routerLink]')
export class RouterLinkReplaceClass {
  // add class `my-active` when `myActiveClass` is `true`
  @HostBinding('class.my-active') 

  // read `router-link-active` class state
  @Input('class.router-link-active') 

  myActiveClass: bool = false;
}

Plunker example

See also In Angular 2 how do I assign a custom class to an active router link?

update

because myActiveClass isn't updated when the router-link-active class is added/removed I modified the directive to get the information about the active route the same way as the RouterLink directive:

import {ROUTER_DIRECTIVES, RouteConfig, Router, Instruction} from 'angular2/router';

@Directive({
  selector: '[routerLink]'
})
export class RouterLinkReplaceClass {

  //@Input('class.router-link-active')
  // myActiveClass: boolean = false;
  private _navigationInstruction: Instruction;
  @Input('routerLink')
  private _routeParams: any[];

  constructor(private _router: Router) {
    // we need to update the link whenever a route changes to account for aux routes
    this._router.subscribe((_) => this._updateLink());
  }

  private _updateLink(): void {
    this._navigationInstruction = this._router.generate(this._routeParams);
  }

  @HostBinding('class.my-active')
  get isRouteActive(): boolean {
    return this._navigationInstruction ? this._router.isRouteActive(this._navigationInstruction) : null;
  }
}
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thank you for such a quick answer! But your example looks a bit artificial because you set "router-link-active" manually. I've edited it to show what I'd like to get: https://plnkr.co/edit/UU0mFn?p=preview I expect that current active link will have both yellow background and red border but something goes wrong again. Is it possible at all? – N. Kudryavtsev Apr 11 '16 at 12:27
  • I tried to keep it simple. Thanks for your Plunker link. It's weird that the `@Input()` is not updated when the class is set by the router. Looks like a bug to me. I'm still investigating. – Günter Zöchbauer Apr 11 '16 at 12:51
  • I can't believe changing a class name could be so difficult... Great work! But there is more to come.. I have one router-outlet inside another (https://plnkr.co/edit/pEAWUP?p=preview). I tried to leave or delete RouterLinkReplaceClass in Some's directives array but it doesn't help. Could you see it once again please? – N. Kudryavtsev Apr 14 '16 at 21:47
  • @GünterZöchbauer I've added updated answer to work with rc1. I'll delete it when you update yours, as credits go to you. – František Žiačik May 15 '16 at 12:12
  • No worries. Thanks for the hint. No need to delete. – Günter Zöchbauer May 15 '16 at 12:13
2

Based on Günter Zöchbauer's answer, after breaking changes in angular2 rc1, this is working for me now.

import { Directive, Input, HostBinding, OnInit } from '@angular/core';
import { Router, RouteSegment, UrlTree } from '@angular/router';

@Directive({
    selector: '[routerLink]'
})
export class RouterActiveClass implements OnInit {
    private currentUrl: UrlTree;
    @Input('routerLink') private routerLink: any[];

    constructor(private routeSegment: RouteSegment, private router: Router) {
        this.router.changes.subscribe(() => this.updateCurrentUrl());
    }

    private updateCurrentUrl(): void {
        this.currentUrl = this.router.createUrlTree(this.routerLink, this.routeSegment);
    }

    @HostBinding('class.active')
    get isRouteActive(): boolean {
        return this.currentUrl ? this.router.urlTree.contains(this.currentUrl) : null;
    }

    ngOnInit() {
        this.updateCurrentUrl();
    }
}
František Žiačik
  • 7,511
  • 1
  • 34
  • 59