Avatar ·

Как получить параметры запроса (query params) из URL в Angular 2+?

Всем привет! Я только начал изучать Angular (версия 2, кажется, или 4, сейчас пишу на 17-й, но суть та же). Столкнулся с простой, на первый взгляд, задачей: мне нужно прочитать параметры из URL-строки браузера. У меня есть страница со списком товаров, и я хочу, чтобы пользователь мог фильтровать их через query-параметры. Например, URL выглядит так: `/products?category=electronics&sort=price`. Я пытался получить эти параметры внутри компонента, но ничего не работает. Пробовал использовать `ActivatedRoute`, но он возвращает пустой объект или `undefined`. Вот мой код компонента (упрощенно): ```typescript import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-products', template: '

Список товаров

' }) export class ProductsComponent implements OnInit { constructor(private route: ActivatedRoute) {} ngOnInit() { // Пытаюсь получить параметры console.log(this.route.snapshot.queryParams); // Выводит: {} } } ``` Я также пробовал подписаться на `queryParams` через `subscribe`: ```typescript this.route.queryParams.subscribe(params => { console.log(params); // Тоже пусто }); ``` Подскажите, пожалуйста, в чем может быть дело? Может быть, я неправильно настроил маршрутизацию или не тот модуль импортировал? Буду благодарен за подробное объяснение с примерами. **Дополнительная информация:** - Angular версии 17 - Использую `RouterModule.forRoot(routes)` в `AppModule` - Маршрут для `/products` определен как `{ path: 'products', component: ProductsComponent }` Заранее спасибо! <<>>

Подробное решение: получение query-параметров в Angular

Ваш код в целом правильный, но есть несколько важных нюансов, которые нужно учесть. Давайте разберем все по порядку.

1. Правильный импорт и настройка маршрутизации

Убедитесь, что у вас импортирован RouterModule в корневом модуле приложения:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { ProductsComponent } from './products/products.component';

const routes: Routes = [
  { path: 'products', component: ProductsComponent },
  { path: '', redirectTo: '/products', pathMatch: 'full' }
];

@NgModule({
  declarations: [AppComponent, ProductsComponent],
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes) // <-- обязательно
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

2. Использование ActivatedRoute (правильный способ)

Вот рабочий пример компонента, который правильно получает query-параметры:

// products.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-products',
  template: `
    <div>
      <p>Категория: {{ category }}</p>
      <p>Сортировка: {{ sort }}</p>
    </div>
  `
})
export class ProductsComponent implements OnInit, OnDestroy {
  category: string = '';
  sort: string = '';
  private querySubscription: Subscription;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    // Вариант 1: через snapshot (однократное чтение)
    const params = this.route.snapshot.queryParams;
    this.category = params['category'] || '';
    this.sort = params['sort'] || '';
    console.log('Snapshot params:', params);

    // Вариант 2: через subscribe (реактивный, обновляется при изменении URL)
    this.querySubscription = this.route.queryParams.subscribe(params => {
      this.category = params['category'] || '';
      this.sort = params['sort'] || '';
      console.log('Subscribed params:', params);
    });
  }

  ngOnDestroy() {
    // Важно: отписываемся от подписки, чтобы избежать утечек памяти
    if (this.querySubscription) {
      this.querySubscription.unsubscribe();
    }
  }
}

3. Почему ваш код мог не работать?

  • Проблема с импортом: Убедитесь, что ActivatedRoute импортирован из @angular/router, а не из другого пакета.
  • Проблема с модулем: Проверьте, что RouterModule.forRoot() вызван в корневом модуле, а не в дочернем.
  • Проблема с версией Angular: В Angular 2-4 API был немного другим. Если вы используете старую версию, попробуйте this.route.params['value'] (но лучше обновитесь).
  • Проблема с URL: Проверьте, что вы действительно переходите по URL с query-параметрами, например: http://localhost:4200/products?category=electronics&sort=price.

4. Альтернативный способ: использование Router

Иногда удобнее получать параметры через сам Router:

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

constructor(private router: Router) {}

ngOnInit() {
  // Получаем текущий URL и парсим его
  const urlTree = this.router.parseUrl(this.router.url);
  const queryParams = urlTree.queryParams;
  console.log('Router params:', queryParams);
  // { category: 'electronics', sort: 'price' }
}

5. Работа с queryParamsMap (Angular 14+)

В современных версиях Angular появился queryParamsMap, который возвращает ParamMap:

this.route.queryParamMap.subscribe(paramMap => {
  this.category = paramMap.get('category') || '';
  this.sort = paramMap.get('sort') || '';
  // Проверка наличия параметра
  if (paramMap.has('category')) {
    console.log('Категория указана');
  }
  // Получение всех значений (если параметр повторяется)
  const allCategories = paramMap.getAll('category');
  console.log(allCategories); // ['electronics']
});

Итоговая рекомендация

Используйте route.queryParams.subscribe() с обязательной отпиской в ngOnDestroy() (или оператор takeUntil из RxJS). Это самый надежный и реактивный способ. Если вам нужно прочитать параметры только один раз при инициализации компонента — подойдет snapshot.

Не забудьте также проверить, что ваш компонент действительно находится внутри маршрутизируемого модуля и что вы не используете useHash: true в конфигурации маршрутизации (это меняет формат URL).

<<>>

Альтернативный подход: использование Location и HttpParams

Если по каким-то причинам вы не можете или не хотите использовать ActivatedRoute (например, в сервисе или вне компонента маршрута), можно воспользоваться сервисом Location из Angular и классом HttpParams из @angular/common/http:

import { Injectable } from '@angular/core';
import { Location } from '@angular/common';
import { HttpParams } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class QueryParamsService {
  constructor(private location: Location) {}

  getQueryParams(): Record<string, string> {
    // Получаем текущий путь с query-строкой
    const path = this.location.path();
    // Извлекаем часть после '?'
    const queryString = path.split('?')[1] || '';
    // Используем HttpParams для парсинга
    const params = new HttpParams({ fromString: queryString });
    
    const result: Record<string, string> = {};
    params.keys().forEach(key => {
      result[key] = params.get(key) || '';
    });
    
    return result;
  }
}

Когда это полезно:

  • В сервисах, которые не имеют доступа к ActivatedRoute
  • При работе с глобальными query-параметрами (например, ?lang=ru)
  • Когда нужно получить параметры до того, как компонент активирован

Недостатки:

  • Не реактивный — нужно вызывать вручную при изменении URL
  • Не интегрирован с роутером, поэтому не сработает при навигации через router.navigate()

Этот подход стоит использовать только как fallback, когда стандартный способ через ActivatedRoute недоступен.