Angular: сервисы, инъекции и получение данных с серверва внутри сервиса

Сервисы – это классы, которые выполняют особые задачи. Например, выполняют запросы на сервер и возвращают данные в виде обсервеблов, содержат сабджекты и методы для работы с ними, а также они способны выполнять передачу данных между несвязанными компонентами. Своего рода играют роль глобального стора, имхо, позволяя обходиться без библиотек типа редакса (в Ангуляре это ngRx и другие менее известные).

1. Создание сервиса

Создаем файл сервиса в папке с компонентом (дочерным или корневым, зависимо от “глобальности” самого сервиса) nameComponent/nameComponent.service.ts:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

export interface Movies {
  page: number;
  results: any[];
  total_result: number;
  total_pages: number;
}

export interface NavItems {
  title: string;
  icon: string;
  genreId: number;
  isActive: boolean;
}

@Injectable()
export class FurnitureService {
  constructor(private http: HttpClient) {}

  navItems: NavItems[] = [
    { title: 'Action', icon: 'weekend', genreId: 28, isActive: true },
    { title: 'Adventure', icon: 'bed', genreId: 12, isActive: false },
    { title: 'Comedy', icon: 'table', genreId: 35, isActive: false },
    { title: 'Drama', icon: 'event_seat', genreId: 18, isActive: false },
    { title: 'Family', icon: 'single_bed', genreId: 10751, isActive: false },
  ];

  getMoviesList(genreId: number): Observable<Movies> {
    return this.http.get<Movies>(
      `https://api.themoviedb.org/3/discover/movie?api_key=2457bcf1079900ec3973765a5a***&with_genres=${genreId}&page=1`
    );
  }
}

Данный сервис содержит пукнты меню для навигации (navItems), а также функцию для получения данных с сервера по запросу с компоненты.

Директива @Injectable() нужна для сервисов, которые подключаются в другие сервисы (объявляются в них аналогично, как внутри компонент) для совмесной работы. Но в примере выше эта директива присутствует, так как без нее выдает ошибку, видимо это связано из наблюдателем (Observable) сервера.

2. Подключаем сервер к основному компоненту

Под основным имеется в виду корневой (app.component.spec.ts) или какой-то другой важный, у которого обычно еще есть дочерные. В результате потом соединять с сервисом можно дочерные компоненты. Но есть важные нюансы, которые описаны будут ниже.

nameComponent.component.ts:

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

import { FurnitureService, Movies } from './furniture.service'; <------- здесь

@Component({
  selector: 'app-furniture',
  templateUrl: './furniture.component.html',
  styleUrls: ['./furniture.component.scss'],
  providers: [FurnitureService], // <------- здесь
})
export class FurnitureComponent implements OnInit {
  activeNavItemIndex: number = 0;
  genreId = 28;
  moviesList: Movies = {
    page: 0,
    results: [],
    total_result: 0,
    total_pages: 0,
  };
  navItems = this.furnitureService.navItems;

  constructor(private furnitureService: FurnitureService) {} <------- здесь

  getFurnitureList(genreId: number = this.genreId): void {
    this.furnitureService
      .getMoviesList(genreId)
      .subscribe((movies: Movies) => (this.moviesList = movies));
  }

  ngOnInit(): void {
    this.getFurnitureList();
  }

  changeNavItem(idx: number) {
    this.navItems[this.activeNavItemIndex].isActive = false;
    this.activeNavItemIndex = idx;
    this.navItems[idx].isActive = true;
    this.genreId = this.navItems[idx].genreId;
    this.getFurnitureList();
  }
}

Стрелочками в комментариях в блоке кода показаны 3 места, где надо упомянуть сервер. Теперь к нему можно обращаться, как к классу, получая с него данные и используюя методы для изменения этих данных.

А в функции getFurnitureList видно, как можно брать данные со слушателя. Если бы мы данные с сервера получали прямо здесь, то проблем таких бы не было и код получения данных выглядел бы проще:

getMoviesList(): void {
   this.http
     .get<Movies>(
       `https://api.themoviedb.org/3/discover/movie?api_key=2457bcf1079900ec3973765a5a***&with_genres=${this.genreId}&page=1`
     )
     .subscribe((response) => {
       this.moviesList = response;
     });
 }

3. Подключение в других компонентах (дочерных к этому)

В дочерных компонентах подключается сервис практически таким же образом, за исключением того, что его не надо добавлять в providers (без особой необходимости)! Дело в том, что тогда при выполнении методов для изменения массива, отображающий элементы из этого массива компонент не будет обновляться! А если не добавлять в  providers, то такой проблемы не будет.

Связь двух любых компонент, подключенных к одному сервису

Все будет работать хорошо! Но более правильный способ для такой связки – Observable!!!

Компоненты могут создавать события и подписываться на них, если они подключены к одному сервису.

1. Создаем генератор событий в самом сервисе по аналогии с опрокидыванием данных вверх, но без прописывания в начале @Output():

statusUpdated = new EventEmitter<string>();

2. В компоненте, которая будет генерировать событие (отправлять данные) вызываем этот созданный экземпляр класса и присваем передаваемое значение свойству emit:

//в provide класс accountsService.statusUpdated можно не добавлять, если выше он уже добавлен
this.accountsService.statusUpdated.emit(status)

3. Теперь подписываемся на изменения статуса в другой компоненте, где тоже импортирован этот класс, и назначем функцию для обработки полученных данных:

//в provide класс accountsService.statusUpdated можно не добавлять, если выше он уже добавлен

this.accountService.statusUpdated.subscribe(
  (status: string) => alert('New Status ' + status)
);

Похожую связку можно сделать и при помощи Sybject.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *