首页 > 解决方案 > mat-autocomplete 不使用 observable 过滤

问题描述

我有两个反应 Angular 形式的 mat-autocomplete 下拉菜单(Angular 和 Angular Material v12)。

一个作品。它从服务中提取一组对象,并且不包括选项的 Observable。它的开发与示例类似。另一个是订阅下拉选项的 observable,它们会显示,但无法过滤。我没有看到任何错误。

我的假设是过滤器在数据存在之前触发,或者过滤器的应用方式存在其他问题。数据显示得很好 - 我无法输入和过滤。

我对可观察代码的设置略有不同,以允许 API 上的订阅在过滤器上的订阅之前触发并比较唯一 ID。它似乎没有奏效。我无法确定代码的哪一部分失败了。我试过用可观察的重复工作代码,但它不能以相同的方式过滤类型(需要两个参数),我被困在那里。

也许有一种方法可以简化我的订阅?我明白发生了什么,但我观察到的经验很轻松。相关代码如下:

工作模板:

<mat-grid-tile-header>
    Waste Type
</mat-grid-tile-header>
   <mat-form-field appearance="standard">
      <mat-label>Waste Type</mat-label>
         <input
            tabindex="2"
            #waste_type
            matInput
            [matAutocomplete]="waste_type_auto"
            placeholder="Choose a Waste Type"
            class="input"
            type="text"
            formControlName="waste_type"
            [ngClass]="{'is-success' : collectForm.get('waste_type').valid && collectForm.get('waste_type').dirty, 'is-danger' : !collectForm.get('waste_type').valid }"
             aria-label="Waste Type">
             <mat-autocomplete #waste_type_auto="matAutocomplete">
                <mat-option *ngFor="let waste_type of filteredWaste_Types$ | async" [value]="waste_type.name">
                <img class="example-option-img" aria-hidden [src]="waste_type.icon" height="25">
                <span>{{ waste_type.name }}</span>
                </mat-option>
             </mat-autocomplete>
             <mat-hint [hidden]="collectForm.get('waste_type').valid" align="end">Waste type (e.g., Recycling) is required</mat-hint>
              </mat-form-field>

非工作模板(几乎相同):

<mat-grid-tile-header>
          Tare
        </mat-grid-tile-header>
            <mat-form-field appearance="standard">
              <mat-label>Waste Container</mat-label>
              <input
              tabindex="4"
              #tare_container
              placeholder="Enter Container"
              matInput
              [matAutocomplete]="tare_auto"
              class="input"
              type="text"
              formControlName="tare_container"
              [ngClass]="{'is-success' : collectForm.get('tare_container').valid && collectForm.get('tare_container').dirty, 'is-danger' : !collectForm.get('tare_container').valid }"
               aria-label="Tare Container">
                <mat-autocomplete #tare_auto="matAutocomplete">
                  <mat-option *ngFor="let tare of filteredTares$ | async" [value]="tare.container_name">
                  <img class="example-option-img" aria-hidden [src]="tare.container_icon" height="25">
                  <span>{{ tare.container_name }}</span>
                  </mat-option>
                </mat-autocomplete>
                <mat-hint [hidden]="collectForm.get('tare_container').valid" align="end">Tare (e.g., Lg Compost Bin) is required</mat-hint>
                </mat-form-field>

.ts 用于两个过滤器

filteredWaste_Types$: Observable<Waste_Type[]>;
filteredTares$: Observable<Tare[]>;

// Working Value Changes Subscription

this.filteredWaste_Types$ = this.collectForm.get('waste_type').valueChanges
        .pipe(
          startWith(''),
          map(value => this._filteredWaste_Types(value))
        );

// Non-Working Value Changes Subscription

this.filteredTares$ = zip(
       this.collectForm.get('tare_container').valueChanges
       .pipe(
         startWith('')),
         this.ts.tares,
        )
        .pipe(
          map(([value, tares]) => {
          const filtered = value ? this._filteredTares(value, tares) : tares;
          return filtered;
        })
      );

// Working Filter

private _filteredWaste_Types(value: string): Waste_Type[] {
    const filterValue = value.toLowerCase();
    return this.wts.waste_types.filter(waste_type => waste_type.name.toLowerCase().includes(filterValue));
}

// Non-Working Filter
    
private _filteredTares(value: string, tares: Tare[]): Tare[] {
     const filterValue = value.toLowerCase();
     return tares.filter(tare => tare.container_name.toLowerCase().includes(filterValue));
}

工作废物类型服务:

waste_types: Waste_Type[] = [
  {
    name: 'Batteries',
    icon: './assets/img/battery_icon.png'
  },
  {
    name: 'Cardboard',
    icon: './assets/img/cardboard_icon.png'
  },
  {
    name: 'Compost',
    icon: './assets/img/compost_icon.png'
  }];

非工作皮重服务:

public cachedTares: Tare[] = [];
    
public get tares() {
   return this.api.getTares()
   .pipe(
    map((tares) => {
    this.cachedTares = tares;
    return tares;
    })
    );
    };
    

提供非工作服务的 API:

public getTares() {
  const uri = '/containers';
  if (this.isOnline) {
    return this.http.get<Tare[]>(uri, this.httpOptions).pipe(
    map(this.cacheGetRequest(uri)),
    );
    } else {
    const result = this.getCachedRequest<Tare[]>(uri);
    return new BehaviorSubject(result).asObservable();
    }
   }

每个的数据结构几乎相同:

export interface Waste_Type {
  name: string;
  icon: string;
}

export interface Tare {
  id: number;
  container_name: string;
  container_icon: string;
  container_weight: number;
}

标签: angularobservablemat-autocomplete

解决方案


我想你只需要用ziprxjscombineLatest运算符替换。

区别在于:

combineLatest操作符的行为与zip类似,但zip仅在每个 Observable源之前发出过一个项目时才会发出, 而combineLatest在任何源 Observable 发出一个项目时发出一个项目

这意味着filteredTares$当您收到来自的响应时,您的 observable 只会发出一次值TareService。之后,即使valueChanges发出新值,它也会保持沉默(即使您输入input

this.filteredTares$ = combineLatest([
  this.collectForm2.get('tare_container').valueChanges.pipe(startWith('')),
  this.ts.tares,
])
...

Stackblitz 示例


推荐阅读