სთეითში მონაცემების ინიციალიზაცია
ჯერ შევქმნათ სერვისი todo.service.ts
, რომელიც გასაკეთებელი საქმეების სიის მენეჯმენტზე
იზრუნებს, ისევე როგორც ბექენდიდან ამ სიის მიღება-მოდიფიკაციაზე. მივხედოთ სთეითის
ინიციალიზაციის ლოგიკას. ანუ ჩვენ გვინდა, რომ როცა აპლიკაცია გაიხსნება, ჩაიტვირთოს
გასაკეთებელი საქმეები.
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { BehaviorSubject, catchError, of, tap } from "rxjs";
export interface TodoItem {
id: number;
title: string;
done: boolean;
}
@Injectable({ providedIn: "root" })
export class TodoService {
private url = "http://localhost:3000/todos";
private todos$ = new BehaviorSubject<TodoItem[]>([]);
constructor(private http: HttpClient) {}
get todos() {
return this.todos$.asObservable();
}
public init() {
this.http
.get<TodoItem[]>(this.url)
.pipe(
tap((todos) => {
this.todos$.next(todos);
})
)
.subscribe();
}
}
ჩვენ აქვე ვქმნით TodoItem-ის ინტერფეისს, რომელიც იმ ტიპს შეესაბამება,
რა ტიპის ობიექტებიც დავამატეთ database.json
-ში.
სერვისში ვინახავთ მისამართს, რომელიც ენდფოინთის მიხედვით აცნობებს
json-server
-ს JSON ფაილში რომელი თვისებიდან ამოიღოს ინფორმაცია.
ვქმნით todo$
-ს რომელიც იქნება BehaviorSubject
, ის დააბრუნებს
TodoItem
მასივის ტიპის მონაცემებს და ის თავიდან იქნება ცარიელი მასივი.
კონსტრუქტორში შემოგვაქვს HttpClient
რომლითაც მოთხოვნებს განვახორციელებთ.
აქვე ვმქნით გეთერს, რომელიც ამ ჩვენ todo$
სტრიმს დააბრუნებს asObservable
მეთოდით, ეს საბჯექთის მაგივრად აბრუნებს Observable ინსტანციას, რათა მასზე
კომპონენტებიდან next
მეთოდის დაძახება არ იყოს შესაძლებელი. ჩვენ გვინდა,
რომ next
მეთოდის დაძახება შეიძლებოდეს მხოლოდ სერვისიდან, ამით გაუგებრობებს
ავირიდებთ თავიდან რადგან სხვადასხვა კომპონენტებიდან მისი დაძახება რაღაც ეტაპზე
ქაოსს გამოიწვევს.
ჩვენ ვქმნით init
მეთოდს, რომელსაც შეგვიძლია კომპონენტიდან დავუძახოტ.
ის HTTP მოთხოვნით მიიღებს მონაცემებს და მას tap
ოპერატორით todos$
სტრიმში დაანექსთებს. ასე GET მოთხოვნას მოყვება ეფექტი, რომელიც სთეითის
სტრიმში ახალ მნიშვნელობას გასცემს. მასზე აქვე ვასუბსქრაიბებთ, რადგან
კომპონენტში მონაცემებს მაინც todos$
სტრიმიდან ავიღებთ. ჩვენთვის მთავარია,
რომ ამ მეთოდზე დაძახებამ უბრალოდ მოთხოვნა გაგზავნოს და tap
-ში არსებული
ეფექტი გამოიწვიოს. ანსუბსქრაიბი აქ არ დაგვჭირდება, რადგან ამას HttpClient
აგვარებს.
ახლა AppComponent-ში გამოვიყენოთ ეს სერვისი:
import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import { TodoItem, TodoService } from "./todo.service";
@Component({
selector: "app-root",
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit {
todos$ = this.todoService.todos;
constructor(private todoService: TodoService) {}
ngOnInit(): void {
this.todoService.init();
}
}
აქ ჩვენ @Component
დეკორატორში საინტერესო რაღაცას ვამატებთ:
changeDetection
-ის სტრატეგიას ვცვლით onPush
-ზე (რომელიც უნდა დავაიმპორტოთ).
ეს ეუბნება ანგულარის აპლიკაციას, რომ ავტომატურად არ დააფიქსიროს ცვლილებები და
არ დაარენდეროს თემფლეითი. ამის გამოყენება უაცილებელი არ არის, მაგრამ რეაქტიული
მიდგომის დროს ეს რესურსებს ზოგავს, რადგან ჩვენ შეგვიძლია ცვლილებების დეტექტორი
მაშინ დავატრიგეროთ, როცა ამის საჭიროება ნამდვილად არსებობს. ეს მეთოდი სირთულეებს
შექმნის, თუ ჩვენ სტრიმებს და async
ფაიფებს არ ვიყენებთ, თუმცა მათი გამოყენების
შემთხვევაში აპლიკაცია შედარებით უფრო სწრაფია და ნაკლებ რესურსებს საჭიროებს.
ანგულარში რეაქტიული პროგრამირების დროს ფაქტობრივად ყველა კომპონენტი onPush
-ზე გვაქვს.
ჩვენ ვაინჯექთებთ TodoService
-ს და კომპონენტის თვისებაში ვინახავთ ნივთების
სტრიმს, რომელსაც გეთერი გვიბრუნებს.
აპლიკაციის ინიციალიზაციისას სერვისზე ვეძახით init
მეთოდს, რომელის შედეგადაც
ჩვენ კომპონენტის todos$
სტრიმში მონაცემები უნდა მივიღოთ, რა თქმა უნდა ეს იმ
შემთხვევაში, თუ ამ სტრიმზე დავასუბსქრაიბებთ. კონვენციურად ყოველთვის უნდა ვცადოთ
თემფლეითში async
ფაიფით დასუბსქრაიბება იმის მაგივრად, რომ მსაზე კლასში
დავასუბსქრაიბოთ.
<div class="container" style="max-width: 500px">
<h1>Your List:</h1>
<ul class="list-group" *ngIf="todos$ | async as todos">
<p *ngIf="todos.length === 0">Your list will show here...</p>
<li
class="list-group-item d-flex justify-content-between align-items-center"
*ngFor="let item of todos"
>
<div class="d-flex align-items-center">
<input type="checkbox" [checked]="item.done" />
<span class="ms-2">{{ item.title }}</span>
</div>
</li>
</ul>
</div>
ჩვენ გამოვსახავთ სიას ngIf
დირექტივით, სადაც async
ფაიფით სტრიმზე ვასუბსქრაიბებთ
და მისი შედეგი გამოგვაქვს, როგორც todo
ცვლადი. შემდეგ ამ ცვლადზე ვაკეთებთ
ლუპს და მათ გამოვსახავთ. ჩვენ ვქმნით ჩექბოქსს ყოველი ნივთისთვის, რომელიც მინიშნული
იქნება, თუ მისი done
თვისება ჭეშმარიტია. თუ მასივი ცარიელია, ჩვენ ტექსტით ამაზე
მივანიშნებთ. async
ფაიფის ერთ-ერთი პლიუსი ის არის, რომ ის view-ს ხელახლა
დარენდერებას მაშინ აიძულებს, როცა მასში გატარებული სტრიმი ახალ მნიშვნელობას გასცემს.
თუ ბრაუზერს შევხედავთ, ნივთების სია უნდა გამოისახოს. ახლა ახალი ნივთების დამატებას მივხედოთ.