简体   繁体   English

mat-autocomplete 不使用 observable 过滤

[英]mat-autocomplete not filtering with observable

I have two mat-autocomplete drop downs in a reactive Angular form (Angular and Angular Material v12).我有两个响应式 Angular 形式的 mat-autocomplete 下拉菜单(Angular 和 Angular Material v12)。

One works.一个作品。 It is pulling an array of objects from a service and does not include an Observable for the options.它从服务中提取一组对象,并且不包括选项的 Observable。 It is developed similarly to the example .它的开发与示例类似。 The other one is subscribing to an observable for the drop down options, and they show up, but cannot filter.另一个是订阅下拉选项的 observable,它们显示出来,但不能过滤。 I see no errors.我看不到任何错误。

My hypothesis is that the either the filter is firing before the data is there, or there is some other issue with how the filter is applied.我的假设是过滤器在数据存在之前触发,或者过滤器的应用方式存在其他问题。 The data shows up just fine - I just can't type and filter.数据显示得很好 - 我只是无法输入和过滤。

I set up the observable code slightly differently to allow the subscription on the API to fire before the subscription on the filter and to compare the unique ID.我以稍微不同的方式设置了可观察代码,以允许 API 上的订阅在过滤器上的订阅之前触发并比较唯一 ID。 It doesn't seem to have worked.它似乎没有奏效。 I cannot determine which part of the code is failing.我无法确定代码的哪一部分失败了。 I have tried repeating the working code with the observable, but it can't filter the type in the same way (takes two arguments), and I'm stuck there.我试过用 observable 重复工作代码,但它不能以相同的方式过滤类型(需要两个参数),我被困在那里。

Perhaps there is a way to simplify my subscription?也许有一种方法可以简化我的订阅? I understand what is going on, but my observable experience is light.我明白发生了什么,但我可观察到的经验很浅。 Relevant code below:相关代码如下:

Working template:工作模板:

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

Non-working template (virtually the same):非工作模板(几乎相同):

<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 for both filters .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));
}

Working waste_type service:工作垃圾类型服务:

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

Non-working tare service:非工作去皮服务:

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

API that serves non-working service:服务非工作服务的 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();
    }
   }

The data structure of each is virtually the same:每个的数据结构几乎相同:

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

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

I guess you just need to replace zip with combineLatest rxjs operator.我想你只需要用combineLatest rxjs 运算符替换zip即可。

The difference is:区别在于:

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 combineLatest操作符的行为与zip类似,但zip仅在每个 Observable源之前发射过一个项目时才发射,而combineLatest在任何源 Observables发射一个项目时发射一个项目

It means that your filteredTares$ observable emits value only once when you received response from TareService .这意味着当您收到来自TareService响应时,您的TareService filteredTares$ observable 只发出一次值。 After that it keeps silence even if valueChanges is emitting new values(even when you type in input )之后,即使valueChanges正在发出新值,它也会保持沉默(即使您input

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

Stackblitz Example Stackblitz 示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM