QueryParams

QueryParams

წინა ქვეთავში ჩვენ მარტივი პარამეტრები გამოვიყენეთ, მაგრამ ეს განსხვავდება Query პარამეტრებისგან. მისი საშუალებით შეგვიძლია აპლიკაციის სთეითი, იგივე მდგომარეობა შევინახოთ მისამართში. ამ მაგალითში განვიხილავთ როგორ შეგვიძლია პროდუქტების სორტირების მიმართულება შევინახოთ QueryParams-ში.

ჩვენ ფაქტობრივად წინა თავის იდენტური აპლიკაცია გვაქვს, თუმცა ყურადღებას მხოლოდ ერთ კომპონენტს მივაქცევთ: ProductsComponent რომელიც იყენებს ProductsService-ს რათა პროდუქტები განათავსოს სთეითში.

სერვისში ინახება პროდუქტების მასივი, რომლებსაც გააჩნიათ აიდი, სახელი, აღწერა, სურათი და ფასი. აქვე არსებობს მეთოდი ამ პროდუქტების მისაღებად.

TS
import { Injectable } from "@angular/core";
import { Product } from "./product.model";

@Injectable({
  providedIn: "root",
})
export class ProductsService {
  private products: Product[] = [
    {
      id: 0,
      name: "Lenovo ThinkPad T14",
      price: 899,
      description:
        'Gen 2 14" FHD (Intel 4-Core i5-1135G7, 16GB RAM, 512GB SSD, UHD Graphics) IPS Business Laptop, Backlit, Fingerprint, 2 x Thunderbolt 4, Webcam, 3-Year Warranty, Windows 11 Pro ',
      image: "https://example.com",
    },
    {
      id: 1,
      name: "Dell XPS 13 9310",
      price: 1200,
      description:
        "Touchscreen Laptop - 13.4-inch UHD+ Display, Thin and Light, Intel Core i5-1135G7, 8GB LPDDR4x RAM, 512GB SSD, Intel Iris Xe, Killer Wi-Fi 6 with Dell Service, Win 11 Home - Silver ",
      image: "https://example.com",
    },
    {
      id: 2,
      name: 'HP Envy 17.3"',
      price: 1246,
      description:
        "FHD Touchscreen Laptop, Intel Core i7-1165G7, 64GB RAM, 2TB SSD, Backlit Keyboard, Intel Iris Xe Graphics, Fingerprint Reader, Webcam, Windows 11 Pro, Silver, 32GB USB Card ",
      image: "https://example.com",
    },
  ];

  getAllProducts() {
    return this.products;
  }

  getProductById(id: number) {
    return this.products.find((product) => product.id === id);
  }
}

პროდუქტის ინტერფეისი product.model.ts-ში ასე გამოიყურება:

TS
export interface Product {
  id: number;
  name: string;
  description: string;
  image: string;
  price: number;
}

როუთინგი კონფიგურირებული გვაქვს, რომ თავიდანვე /products გვერდზე გადავიდეთ და გავხსნათ ProductsComponent. ეს კომპონენტი ასე გამოიყურება:

TS
import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
import { ProductsService } from "../products.service";
import { RouterLink } from "@angular/router";
import { Product } from "../product.model";

@Component({
  selector: "app-products",
  standalone: true,
  imports: [CommonModule, RouterLink],
  templateUrl: "./products.component.html",
  styleUrl: "./products.component.css",
})
export class ProductsComponent {
  products: Product[] = this.productsService.getAllProducts();

  constructor(private productsService: ProductsService) {}
}

აპლიკაციის ინიციალიზაციის დროს ჩვენ პროდუქტების სიას ვიღებთ, და კომპონენტში ვინახავთ. ამ პროდუქტებს უბრალოდ თემფლეითში განვათავსებთ:

HTML
<div class="container">
  <ul>
    <li class="product" *ngFor="let product of products">
      <h3>{{ product.name }}</h3>
      <img [src]="product.image" [alt]="product.name" />
      <p>{{ product.price | currency }}</p>
      <a routerLink="/products/{{ product.id }}">Details</a>
    </li>
  </ul>
</div>

ჩვენი მიზანია, რომ მომხმარებელს პროდუქტების გაფილტვრის საშუალება მივცეთ.

ProductsComponent-ში დავამატოთ შემდეგი თვისებები:

TS
  sortBy: 'cheapest' | 'expensive' = 'cheapest';
  sortOptions = ['cheapest', 'expensive'];

ეს იქნება სორტირების მიმართულებები: ჯერ იაფი პროდუქტები გამოჩნდეს თუ ძვირი. თავდაპირველად სორტირება cheapest-ზე იქნება. ასევე გვინდა სორტირების ვარიანტების მასივი, რომლითაც ფორმას ავაგებთ.

კომპონენტის იმპორტებში შემოვიტანოთ FormsModule რათა ფორმების გამოყენება შევძლოთ:

TS
import { FormsModule } from "@angular/forms";

@Component({
  imports: [/* ... */ FormsModule],
})

ახლა უბრალოდ შევქმნათ select ელემენტი NgModel დირექტივით, რომელსაც დავაკავშირებთ sortBy თვისებასთან. ngModelChange არის ივენთი, რომელსაც ფორმის ელემენტი დააემითებს, როცა მასში მომხმარებელი რამეს შეცვლის. ამ დროს გვინდა changeSort მეთოდით რეაგირება, რომელსაც ტაიპსკრიპტის ნიმუშში ვნახავთ. NgFor დირექტივით სორტირების ვარიანტები განვათავსოთ და მათი მნიშვნელობები მივაბათ ელემენტს. ასევე ის ინტერპოლაციით გამოვსახოთ. დაკლიკებაზე უნდა გააქტიურდეს მეთოდი, რომლითაც სორტირება მოხდება.

HTML
<div class="container">
  <label for="sort-select">Sort by</label>
  <select
    id="sort-select"
    [ngModel]="sortBy"
    (ngModelChange)="changeSort($event)"
  >
    <option *ngFor="let sortOption of sortOptions" [value]="sortOption">
      {{ sortOption }}
    </option>
  </select>
  <ul>
    <li class="product" *ngFor="let product of products">
      <h3>{{ product.name }}</h3>
      <img [src]="product.image" [alt]="product.name" />
      <p>{{ product.price | currency }}</p>
      <a routerLink="/products/{{ product.id }}">Details</a>
    </li>
  </ul>
</div>

შევქმნათ changeSort მეთოდი. ჩვენ არსებულ სიას ასე პირდაპირ არაფერს გავუკეთებთ. ჯერ უნდა მოხდეს ნავიგაცია, რომლის საფუძველზეც შეიცვლება მისამართში query პარამეტრები და ჩვენ შემდგომ მასზე უნდა ვირეაგიროთ.

TS
import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
import { ProductsService } from "../products.service";
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
import { Product } from "../product.model";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormsModule } from "@angular/forms";

@Component({
  selector: "app-products",
  standalone: true,
  imports: [CommonModule, RouterLink, FormsModule],
  templateUrl: "./products.component.html",
  styleUrl: "./products.component.css",
})
export class ProductsComponent {
  sortBy: "cheapest" | "expensive" = "cheapest";
  sortOptions = ["cheapest", "expensive"];
  products: Product[] = [];

  constructor(
    private productsService: ProductsService,
    private router: Router,
    private route: ActivatedRoute
  ) {
    this.route.queryParams
      .pipe(takeUntilDestroyed())
      .subscribe((queryParams) => {
        const unsortedProducts = this.productsService.getAllProducts();
        const sortBy = queryParams["sortBy"];
        this.sortBy = sortBy;
        this.products = this.sortProducts(unsortedProducts, sortBy);
      });
  }

  sortProducts(products: Product[], sortBy: "cheapest" | "expensive") {
    if (sortBy === "cheapest") {
      return products.sort((a, b) => a.price - b.price);
    } else {
      return products.sort((a, b) => b.price - a.price);
    }
  }

  changeSort(newSortBy: string) {
    this.router.navigate(["products"], {
      queryParams: { sortBy: newSortBy },
    });
  }
}

changeSort მეთოდი იყენებს კომპონენტში დაინჯექტებული Router-ის ინსტანციას და მასზე უძახებს navigate მეთოდს. ეს არის routerLink-ის მსგავსი ხელსაწყო, ოღონდ კლასში. პირველ არგუმენტად ეს მეთოდი იღებს მისამართის ცალკეული ერთეულების მასივს. ჩვენ იმავე გვერდზე, prodocts-ზე გვინდა ნავიგაცია, უბრალოდ უნდა შევცვალოთ პარამეტრები. მეორე არგუმენტად ფუნქცია იღებს კონფიგურაციის ობიექტს. აქ ერთ-ერთი თვისებაა queryParams სადაც შეგვიძლია პარამეტრების ობიექტის შექმნა, რომელსაც ანგულარი გარდასახავს პარამეტრებში მისამართის სტრინგად.

აქ ჩენ შევქმენით sortBy პარამეტრი, რომლის მნიშვნელობაც იქნება მომხმარებლის მიერ არჩეული სორტირების მიმართულება. მისამართში ეს გამოჩნდება, როგორც /products?sortBy=cheap.

შემდეგ ჩვენ გვინდა ამ რაუთის ცვლილებაზე რეაგირება. სწორედ ამიტომ constructor-ში ვიყენებთ დაინჯექთებულ ActivatedRoute-ს, რათა დავასუბსქრაიბოთ მასში არსებულ queryParams-ზე. ჩვენ პროდუქტების სიას ვინახავთ ლოკალურ ცვლადში და ასევე queryParams-იდან ვიღებთ ქოლბექში ხელმისაწვდომ პარამეტრების ობიექტს. აქ ჩვენი შექმნილი პარამეტრის მნიშვნელობა შეგვიძლია ავიღოთ - sortBy. ჩვენ ამ სორტირების ვარიანტს კლასის თვისებაში ვანახლებთ, რათა გვერდის დარეფრეშებაზე ფორმაში ისევ სწორი ვარიანტი იყოს არჩეული. რაც მთავარია, ახლა სორტირების მიმართულების მიხედვით ვასორტირებთ პროდუქტებს და მათ კლასის თვისებაში ვინახავთ. სორტირებას ვაკეთებთ sortProducts ფუნქციით. აქ პროდუქტებს და სორტირების მიმართულებას ვიღებთ და ამის მიხედვით სათანადოდ სორტირებულ პროდუქტებს ვაბრუნებთ.

ზემოთხსენებული ლოგიკის გაშვება ngOnInit-შიც არის შესაძლებელი, თუმცა takeUntilDestroyed-ის მარტივად გამოსაყენებლად იგივეს გაკეთება კონსტრუქტორშიც შეიძლება.

ყოველ ნავიგაციაზე queryParams-ის საბსქრიფშენშში არსებული მეთოდი აქტიურდება და ანახლებს:

  • მიმდინარე სორტირების მიმართულება: this.sortBy,
  • პროდუქტების მასივს ამ სორტირების მიხედვით: this.products.

შედეგად შეგვიძლია პროდქტების სორტირება queryParams-ით. რა თქმა უნდა შესაძლებელია იმდენი queryParams-ის შექმნა, რამდენიც მოგვესურვება.

შეჯამება

ამ თავში ჩვენ ვისწავლეთ კლასში Router-ის გამოყენება queryParams-ით და ActivatedRoute-ზე queryParams-ის ცვლილებებზე რეაგირება. ჩვენ მომხმარებლის მიერ სორტირების მიმართულების შეცვლის საფუძველზე ვახორციელებთ ნავიგაციას განახლებული queryParams-ით. queryParams-ის ცვლილებას ამავე კომპონენტში ვუსმენთ და სათანადოდ ვრეაგირებთ: ვანახლებთ სორტირების მიმართულებას და ვასორტირებთ პროდუქტების სიას.