Панель поиска как услуга в angular 7 - PullRequest
0 голосов
/ 02 марта 2020

Прежде чем задать этот вопрос, я провел здесь небольшое исследование и обнаружил, что люди спрашивают о передаче поисковых значений в API отдыха и получении результата обратно.
Моя проблема совершенно иная. У меня есть два компонента и сервис:

  • Компонент панели поиска (отвечает за получение пользовательского ввода при каждом нажатии)
  • Компонент списка приложений (отображает все приложения / только отфильтрованные приложения)
  • Служба приложений, отвечающая за однократное получение всех пользовательских приложений из бэкэнда, а затем получение поискового ввода из компонента панели поиска и обновление поведения объекта.

В любой момент мне нужно сохранить копию оригинального списка приложений, чтобы отфильтровать его.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, distinctUntilChanged, filter } from 'rxjs/operators';
import { Observable, BehaviorSubject } from 'rxjs';
import { Application } from '../models/apps.model';

@Injectable({
  providedIn: 'root'
})
export class AppsService {
  private appListSubject: BehaviorSubject<Application[]>;
  private filteredAppListSubject: BehaviorSubject<Application[]>;
  private appList: Observable<Application[]>;

  constructor(private httpClient: HttpClient) {
    this.appListSubject = new BehaviorSubject<Application[]>({} as Application[]);
    this.filteredAppListSubject = new BehaviorSubject<Application[]>({} as Application[]);
    this.appList = this.filteredAppListSubject.asObservable().pipe(distinctUntilChanged());
  }

  getApps(): Observable<Application[]> {
    return this.httpClient.get('http://localhost:3000/api/user/apps').pipe(map(
      (res: any) => {
        this.setAppList = res;
        return res;
      }
    ));
  }

  public get getCurrentAppList() {
    return this.appList;
  }

  public set setAppList(apps: Application[]) {
    this.appListSubject.next(apps);
    this.filteredAppListSubject.next(apps);
  }

  public searchApp(searchTerm: string) {
    const filteredApps = this.appListSubject.pipe(filter(
      (app: any) => {
        if (app.name.includes(searchTerm)) {
          return app;
        }
      }
    ));
    this.filteredAppListSubject.next(filteredApps);
  }
}

Это моя текущая реализация службы приложений.

Моя цель - сделать так, чтобы компонент applist подписывался на appList, и всякий раз, когда компоненты панели поиска запрашивают у службы поиск значения, компонент applist получает новый массив приложений из наблюдаемой.

Прямо сейчас, поскольку функция канала возвращает наблюдаемое, я не могу добавить результат в качестве следующего значения субъекта поведения.

Мне также хотелось бы узнать ваше мнение об этой конкретной реализации. Спасибо!

1 Ответ

1 голос
/ 03 марта 2020

Я думаю, что ваш дизайн сделал проблему более сложной, чем она есть.

Поток данных является одним из способов:

HeaderComponent - searchTerm -> AppService - apps -> AppListComponent

Я думаю, вам просто нужен Subject в AppService, который служит прокси. HeaderComponent передаст в него поисковые термины, а AppListComponent получит результаты поиска.

app.service.ts

@Injectable({ providedIn: 'root' })
export class AppService {  

  private apps: Application[];
  private filteredApps$: Subject<Application[]> = 
    new ReplaySubject<Application[]>(1);

  getSearchResults(): Observable<Application[]> {
    return this.filteredApps$.asObservable();
  }

  search(searchTerm: string): Observable<void> {
    return this.fetchApps().pipe(
      tap((apps: Application[]) => {
        apps = apps.filter(app => app.name.toLowerCase().includes(searchTerm));
        this.filteredApps$.next(apps);
      }),
      map(() => void 0)
    );
  }

  private fetchApps(): Observable<Application[]> {
    // return cached apps
    if (this.apps) {
      return of(this.apps);
    }

    // fetch and cache apps
    return this.httpClient.get('http://localhost:3000/api/user/apps').pipe(
      tap((apps: Application[]) => this.apps = apps)
    );
  }
}

Служба приложения будет кэшировать HTTP-ответ в первый раз производится поиск. Когда поисковый термин входит в службу, он выбирает приложения, фильтрует их и передает их по теме. Компонент, в котором перечислены результаты поиска, подпишется на тему.

header.component.ts

constructor(private appService: AppService) {
}

searchTerm = '';

ngOnInit() {
  this.appService.search(this.searchTerm).subscribe();
}

onSearchTermChange(): void {
  this.appService.search(this.searchTerm).subscribe();
}

header.component. html

<input [(ngModel)]="searchTerm" (ngModelChange)="onSearchTermChange()" />

app -list.component.ts

constructor(private appService: AppService) {
}

apps$: Observable<Application[]> = this.appService.getSearchResults();

app-list.component. html

<div *ngFor="let app of apps$ | async">
   {{app.name}}
</div>

DEMO: https://stackblitz.com/edit/angular-nbdts8

...