JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Query-параметры и фрагменты: синтаксис, примеры

Query-параметры и фрагменты: синтаксис, примеры

Модуль 4: Node.js, Next.js и Angular
18 уровень , 6 лекция
Открыта

1. Как выглядят query-параметры и фрагменты в URL

Если вы хоть раз видели URL вида https://site.com/products?page=2&sort=price#reviews — поздравляю, вы уже знакомы с query-параметрами (?page=2&sort=price) и фрагментом (#reviews).

  • Query-параметры — это пара ключ=значение после знака вопроса (?). Они используются для передачи дополнительной информации в адресе: фильтры, сортировка, номера страниц, поисковые запросы и т.д.
  • Фрагмент (или anchor/hash) — это часть после #. Обычно он используется для прокрутки к определённому месту на странице или для управления состоянием на клиенте.

В Angular query-параметры и фрагменты — мощный инструмент для управления состоянием приложения через URL. Например, вы можете сохранять фильтры в адресе, чтобы пользователь мог поделиться ссылкой на отфильтрованный список товаров.

Query-параметры

https://site.com/products?category=books&page=3&sort=price
  • После основного пути (/products) идёт знак вопроса ?
  • Дальше через & перечисляются пары ключ=значение

Фрагмент

https://site.com/products/123#description
  • После основного пути и (возможно) query-параметров идёт знак #
  • После # — имя якоря/фрагмента

Можно комбинировать оба:

https://site.com/products/123?view=full#reviews

2. Использование query-параметров и фрагментов в Angular

В Angular работа с query-параметрами и фрагментами — это не магия, а часть стандартного API маршрутизации. Рассмотрим основные способы работы с ними:

Навигация с query-параметрами и фрагментами

Через шаблон (HTML):

<!-- Ссылка с query-параметрами -->
<a [routerLink]="['/products']" [queryParams]="{category: 'books', page: 2}">Книги, страница 2</a>

<!-- Ссылка с фрагментом -->
<a [routerLink]="['/products', 123]" fragment="reviews">Отзывы</a>

<!-- Ссылка с обоими -->
<a [routerLink]="['/products', 123]" [queryParams]="{view: 'full'}" fragment="description">Описание полностью</a>

Через код (TypeScript):

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

constructor(private router: Router) {}

// Переход с query-параметрами
this.router.navigate(['/products'], { queryParams: { category: 'books', page: 2 } });

// Переход с фрагментом
this.router.navigate(['/products', 123], { fragment: 'reviews' });

// Оба вместе
this.router.navigate(['/products', 123], {
  queryParams: { view: 'full' },
  fragment: 'description'
});

Получение query-параметров и фрагмента в компоненте

Для этого Angular предоставляет сервисы ActivatedRoute и его свойства queryParams и fragment.

Пример получения query-параметров

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-products',
  template: `
    <div>
      <h2>Товары: категория {{ category }}, страница {{ page }}</h2>
    </div>
  `
})
export class ProductsComponent implements OnInit {
  category = '';
  page = 1;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      this.category = params['category'] || '';
      this.page = +params['page'] || 1;
    });
  }
}

Пример получения фрагмента

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-product-detail',
  template: `
    <div>
      <h2>Детали товара</h2>
      <p>Текущий фрагмент: {{ fragment }}</p>
    </div>
  `
})
export class ProductDetailComponent implements OnInit {
  fragment: string | null = null;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.fragment.subscribe(fragment => {
      this.fragment = fragment;
    });
  }
}

3. Практические примеры

Фильтрация и пагинация через query-параметры

Реализация фильтрации товаров по категории и перехода по страницам с сохранением состояния в URL:

Шаблон:

<a [routerLink]="['/products']" [queryParams]="{category: 'books', page: 1}">Книги, стр. 1</a>
<a [routerLink]="['/products']" [queryParams]="{category: 'books', page: 2}">Книги, стр. 2</a>
<a [routerLink]="['/products']" [queryParams]="{category: 'toys', page: 1}">Игрушки, стр. 1</a>

Компонент:

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

@Component({
  selector: 'app-products',
  template: `
    <div>
      <h2>Категория: {{ category }}, страница: {{ page }}</h2>
      <button (click)="nextPage()">Следующая страница</button>
    </div>
  `
})
export class ProductsComponent implements OnInit {
  category = '';
  page = 1;

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

  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      this.category = params['category'] || 'all';
      this.page = +params['page'] || 1;
    });
  }

  nextPage() {
    this.router.navigate([], {
      relativeTo: this.route,
      queryParams: { category: this.category, page: this.page + 1 },
      queryParamsHandling: 'merge', // сохранить остальные параметры
    });
  }
}
Комментарий: Здесь мы используем queryParamsHandling: 'merge', чтобы не терять другие параметры при переходе. Это удобно, если у вас много фильтров.

Прокрутка к якорю с помощью фрагмента

Часто нужно прокрутить страницу к определённому месту (например, к секции "Отзывы"):

HTML:

<a [routerLink]="['/products', 123]" fragment="reviews">Отзывы</a>

<!-- где-то далее на странице -->
<div id="reviews">
  <h3>Отзывы</h3>
  <!-- ... -->
</div>

Angular: автоматическая прокрутка

// В app-routing.module.ts
@NgModule({
  imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled' })],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Теперь при переходе по ссылке с фрагментом страница сама прокрутится к элементу с соответствующим id.

4. Полезные нюансы

Работа с несколькими query-параметрами

Query-параметров может быть сколько угодно — Angular автоматически соберёт их в объект:

<a [routerLink]="['/products']" [queryParams]="{category: 'books', page: 2, sort: 'price', search: 'Angular'}">
  Поиск Angular книг, стр. 2, сортировка по цене
</a>

В компоненте:

ngOnInit() {
  this.route.queryParams.subscribe(params => {
    console.log(params); // {category: 'books', page: '2', sort: 'price', search: 'Angular'}
  });
}

Программное изменение query-параметров без перезагрузки

Иногда нужно менять параметры в адресе без перехода на другой маршрут. Для этого используйте this.router.navigate с пустым массивом маршрута:

this.router.navigate([], {
  relativeTo: this.route,
  queryParams: { page: 3 },
  queryParamsHandling: 'merge', // сохранить остальные параметры
});
Фишка: queryParamsHandling: 'merge' — очень полезная опция, чтобы не «затирать» остальные параметры.

Особенности работы с query-параметрами и фрагментами

  • Все query-параметры — строки! Даже если вы передаёте число, в params оно будет строкой ('2', а не 2). Не забывайте приводить типы при необходимости (+params['page']).
  • Если параметр отсутствует — он просто не попадёт в объект params.
  • Фрагмент — это тоже строка или null, если его нет.
  • Если вы хотите сбросить какой-то параметр, передайте его значение как null или undefined.

5. Типичные ошибки при работе с query-параметрами и фрагментами

Ошибка №1: забыли подписаться на queryParams или fragment.
Если вы просто возьмёте this.route.snapshot.queryParams, то получите значения только на момент инициализации компонента. Для динамического отслеживания изменений (например, пользователь меняет параметры фильтрации через адресную строку), используйте this.route.queryParams.subscribe(...).

Ошибка №2: не используете queryParamsHandling при навигации.
Если вы не укажете queryParamsHandling: 'merge', Angular по умолчанию заменит все query-параметры на новые, а старые пропадут. Это особенно неприятно, если у вас несколько фильтров.

Ошибка №3: забыли про типы — все параметры приходят строками.
Не забывайте приводить значения к нужному типу, иначе можно получить неожиданные баги (например, сравнение '2' === 2 даст false).

Ошибка №4: не включили anchorScrolling для фрагментов.
Без этой опции Angular не будет автоматически прокручивать страницу к нужному элементу при переходе по ссылке с фрагментом.

Ошибка №5: дублирующиеся параметры.
Если вы вручную формируете URL и случайно добавите один и тот же параметр дважды (?page=2&page=3), Angular возьмёт последний, но лучше избегать таких ситуаций.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ