首页 > 解决方案 > 搜索栏作为 Angular 7 中的服务

问题描述

在问这个问题之前,我在这里做了一些研究,只发现有人询问将搜索值传递给一个 REST 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 组件就会从可观察到的应用程序中获取一个新数组。

现在,由于管道函数返回一个 observable,我无法将结果添加为行为主题的下一个值。

我也想听听你对这个特定实现的看法。谢谢!

标签: angularsearchangular-servicesbehaviorsubject

解决方案


我认为您的设计使问题看起来比实际更复杂。

数据流是一种方式:

HeaderComponent-- searchTerm--> AppService-- apps-->AppListComponent

我认为您只需要一个SubjectinAppService作为代理。将向其中HeaderComponent传递搜索词,然后AppListComponent将接收搜索结果。

应用服务.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()" />

应用程序列表.component.ts

constructor(private appService: AppService) {
}

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

应用程序列表.component.html

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

演示:https ://stackblitz.com/edit/angular-nbdts8


推荐阅读