搜索栏作为角度7中的服务

问题描述 投票:0回答:1

在问这个问题之前,我在这里进行了一些研究,只发现人们询问将搜索值传递给其他API并取回结果。我的问题完全不同。我有两个组件和一个服务:

  • 一个搜索栏组件(负责在每次键入时获取用户输入)
  • 应用列表组件(显示所有应用/仅过滤的应用)
  • 一种应用程序服务,负责从后端获取所有用户应用程序一次,然后从搜索栏组件获取搜索输入并更新behaviorSubject。

时刻都需要保留原始应用列表的副本以进行过滤。

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组件都会从可观察对象中获取新的一组应用程序。

现在,由于管道函数返回了一个可观察的对象,因此我无法将结果添加为行为主体的下一个值。

我也希望您对此特定实施有意见。谢谢!

angular search angular-services behaviorsubject
1个回答
0
投票

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

数据流是一种方式:

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

© www.soinside.com 2019 - 2024. All rights reserved.