mat-autocomplete 不过滤 observable

mat-autocomplete not filtering with observable

我有两个反应式 Angular 形式的垫子自动完成下拉菜单(Angular 和 Angular Material v12)。

一个有效。它从服务中提取一组对象,并且不包括用于选项的 Observable。它的开发类似于 example。另一个订阅下拉选项的可观察对象,它们会显示,但无法过滤。我没有看到任何错误。

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

我设置的可观察代码略有不同,以允许 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_type 服务:

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;
}

我想你只需要将 zip 替换为 combineLatest rxjs 运算符即可。

区别是:

The combineLatest operator behaves similarly to zip, but while zip emits only when each Observable source has previously emitted an item, combineLatest emits an item whenever any of the source Observables emits an item

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

,它也会保持沉默
this.filteredTares$ = combineLatest([
  this.collectForm2.get('tare_container').valueChanges.pipe(startWith('')),
  this.ts.tares,
])
...

Stackblitz Example