Avatar ·

Как определить активный маршрут (route) в Angular? Определение активного URL и подсветка ссылок

Привет всем! Я новичок в Angular (версия 15) и пытаюсь сделать навигационное меню с подсветкой активного пункта. У меня есть компонент с несколькими ссылками: ```typescript // navigation.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-navigation', template: ` ` }) export class NavigationComponent {} ``` Я хочу, чтобы активная ссылка получала CSS-класс `active` (например, жирный шрифт или другой цвет). Пробовал использовать `routerLinkActive` — он вроде работает, но не совсем так, как я ожидаю. Иногда подсвечиваются несколько ссылок одновременно, особенно когда у меня есть вложенные маршруты. Например, при переходе на `/users/123` подсвечивается и `/users`, и `/dashboard` (если у дашборда путь `''`). Также мне нужно в компоненте программно определять, какой маршрут сейчас активен, чтобы, например, показывать разные элементы интерфейса в зависимости от текущего URL. Пробовал использовать `Router` и `ActivatedRoute`, но запутался в подписках и не понимаю, как правильно получить текущий путь. Подскажите, пожалуйста, как правильно: 1. Подсветить активную ссылку в шаблоне с помощью встроенных средств Angular? 2. Получить текущий активный маршрут в коде компонента (TypeScript) для дальнейшей логики? Буду благодарен за примеры с объяснением. Заранее спасибо! <<>>

Решение с помощью routerLinkActive (для шаблона)

Angular предоставляет встроенную директиву routerLinkActive для подсветки активных ссылок. Она автоматически добавляет CSS-класс к элементу, когда связанный с ним маршрут активен. Вот как правильно её использовать:

// navigation.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-navigation',
  template: `
    <nav>
      <a routerLink="/dashboard"
         routerLinkActive="active"
         [routerLinkActiveOptions]="{ exact: true }">
        Дашборд
      </a>
      <a routerLink="/users"
         routerLinkActive="active"
         [routerLinkActiveOptions]="{ exact: false }">
        Пользователи
      </a>
      <a routerLink="/settings"
         routerLinkActive="active"
         [routerLinkActiveOptions]="{ exact: true }">
        Настройки
      </a>
    </nav>
  `,
  styles: [`
    .active {
      font-weight: bold;
      color: #007bff;
      border-bottom: 2px solid #007bff;
    }
  `]
})
export class NavigationComponent {}

Ключевой момент — опция exact в routerLinkActiveOptions:

  • { exact: true } — класс добавляется только если URL полностью совпадает с routerLink. Идеально для корневых маршрутов (например, /dashboard).
  • { exact: false } (значение по умолчанию) — класс добавляется если URL начинается с указанного пути. Подходит для родительских маршрутов, когда нужно подсвечивать раздел при переходе на дочерние страницы (например, /users при /users/123).

Если у вас есть пустой путь (''), он будет совпадать с любым URL. Чтобы избежать этого, всегда используйте exact: true для корневого маршрута или задайте явный путь, например /home.

Программное определение активного маршрута в компоненте

Для получения текущего маршрута в TypeScript используйте сервис Router и объект ActivatedRoute. Вот полный пример:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { filter, Subscription } from 'rxjs';

@Component({
  selector: 'app-navigation',
  template: `
    <nav>
      <a routerLink="/dashboard">Дашборд</a>
      <a routerLink="/users">Пользователи</a>
      <a routerLink="/settings">Настройки</a>
    </nav>
    <p *ngIf="isUsersSection">Вы в разделе пользователей</p>
  `
})
export class NavigationComponent implements OnInit, OnDestroy {
  currentUrl: string = '';
  isUsersSection: boolean = false;
  private subscription: Subscription = new Subscription();

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

  ngOnInit() {
    // Подписываемся на события навигации
    this.subscription.add(
      this.router.events.pipe(
        filter(event => event instanceof NavigationEnd)
      ).subscribe((event: NavigationEnd) => {
        this.currentUrl = event.urlAfterRedirects;
        this.isUsersSection = this.currentUrl.startsWith('/users');
        console.log('Текущий URL:', this.currentUrl);
      })
    );

    // Получаем текущий маршрут при инициализации
    this.currentUrl = this.router.url;
    this.isUsersSection = this.currentUrl.startsWith('/users');
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

  // Альтернативный способ через ActivatedRoute
  getCurrentRoutePath(): string {
    let route = this.route.root;
    let path = '';
    while (route.firstChild) {
      route = route.firstChild;
      if (route.snapshot.url.length > 0) {
        path += '/' + route.snapshot.url.join('/');
      }
    }
    return path || '/';
  }
}

Разбор кода:

  • router.events — Observable, который испускает все события роутера. Фильтруем только NavigationEnd, чтобы получить момент завершения навигации.
  • event.urlAfterRedirects — конечный URL после всех редиректов (если они есть).
  • router.url — текущий URL на момент вызова (можно использовать в ngOnInit для начального значения).
  • ActivatedRoute — позволяет пройти по дереву маршрутов и собрать путь из сегментов. Полезно для получения параметров маршрута.

Важно: не забывайте отписываться от подписок в ngOnDestroy, чтобы избежать утечек памяти. Альтернативно можно использовать takeUntil из RxJS.

<<>>

Использование сервиса Location и Router.isActive

Если вам нужно только проверить, активен ли конкретный маршрут в коде компонента (без подписки на все события), можно использовать метод Router.isActive(). Это проще и не требует управления подписками:

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

@Component({
  selector: 'app-navigation',
  template: `
    <button (click)="checkRoute()">Проверить маршрут</button>
    <p *ngIf="isDashboardActive">Дашборд активен</p>
  `
})
export class NavigationComponent {
  isDashboardActive: boolean = false;

  constructor(private router: Router) {
    // Проверяем при инициализации
    this.isDashboardActive = this.router.isActive('/dashboard', true);
  }

  checkRoute() {
    // Проверка с точным совпадением (exact: true)
    this.isDashboardActive = this.router.isActive('/dashboard', true);
    
    // Проверка с частичным совпадением (exact: false)
    const isUsersSection = this.router.isActive('/users', false);
    console.log('В разделе пользователей?', isUsersSection);
  }
}

Преимущества:

  • Нет необходимости подписываться на события — метод возвращает булево значение синхронно.
  • Второй параметр exact работает аналогично routerLinkActiveOptions.
  • Можно использовать в шаблоне через метод, но будьте осторожны — вызов метода в шаблоне может привести к частым перерисовкам. Лучше сохранять результат в свойство.

Недостаток: метод не обновляется автоматически при смене маршрута. Вам нужно вызывать его вручную (например, в обработчике клика или через подписку на NavigationEnd, если требуется реактивность).

Для простых случаев, когда нужно проверить маршрут по событию (например, при клике на кнопку), Router.isActive() — самое лаконичное решение.