angular - 为什么 ngx-mat-select-search 在此示例中不能处理动态数据?

标签 angular typescript

我已经安装了ngx-mat-select-search并遵循 StackBlitz 中的示例.

该示例运行良好,就像在 stackblitz 中一样。然后,我更改了代码以使用我的动态数据。我正常加载数据,并且值列在 UI(搜索下拉列表)中,但搜索栏不执行任何操作,也没有错误。

HTML:

<mat-form-field class="selectList-full-width">
    <mat-select [formControl]="selectTechnicalRoomCtrl" [placeholder]="'SelectTechnicalRoom' | localize" #singleSelect>
        <mat-option>
        <ngx-mat-select-search [formControl]="selectTechnicalRoomFilterCtrl" [placeholderLabel]="'Search' | localize"></ngx-mat-select-search>
        </mat-option>
        <mat-option *ngFor="let technicalRoom of technicalRooms" [value]="technicalRoom">
        {{technicalRoom.nameRoom}}
        </mat-option>
    </mat-select>
</mat-form-field>
<div class="p-r-25 btn-toolbar">
    <button style="margin-left: 10px; padding: 0 1em;" mat-raised-button color="primary" >{{ 'ListPoints' | localize }}</button>    
</div>

TS:

export class HomeComponent extends AppComponentBase implements OnInit, AfterViewInit, OnDestroy {
    technicalRooms: IdNameTechnicalRoomDto[] = [];
    public selectTechnicalRoomCtrl: FormControl = new FormControl();
    public selectTechnicalRoomFilterCtrl: FormControl = new FormControl();

    /** list of itens filtered by search keyword */
    public filteredTechnicalRooms: ReplaySubject<IdNameTechnicalRoomDto[]> = new ReplaySubject<IdNameTechnicalRoomDto[]>(1);

    @ViewChild('singleSelect', { static: true }) singleSelect: MatSelect;

    protected _onDestroy = new Subject<void>();

    constructor(
        injector: Injector,
        private _technicalRoomsService: TechnicalRoomServiceProxy,
    ) {
        super(injector);
    }

    ngOnInit() {
      this.list();

      // set initial selection
    this.selectTechnicalRoomCtrl.setValue(this.technicalRooms[10]);

    // load the initial itens list
    this.filteredTechnicalRooms.next(this.technicalRooms.slice());

    // listen for search field value changes
    this.selectTechnicalRoomFilterCtrl.valueChanges
      .pipe(takeUntil(this._onDestroy))
      .subscribe(() => {
        this.filterTechnicalRooms();
      });
      }

      list(): void {

          this._technicalRoomsService
              .getList()
              .pipe()
              .subscribe(
                data => this.technicalRooms = data["result"]
              );
      }

      ngAfterViewInit() {
        this.setInitialValue();
      }

      ngOnDestroy() {
        this._onDestroy.next();
        this._onDestroy.complete();
      }

      /**
       * Sets the initial value after the filteredTechnicalRooms are loaded initially
       */
      protected setInitialValue() {
        this.filteredTechnicalRooms
          .pipe(take(1), takeUntil(this._onDestroy))
          .subscribe(() => {
            this.singleSelect.compareWith = (a: IdNameTechnicalRoomDto, b: IdNameTechnicalRoomDto) => a && b && a.id === b.id;
          });
      }

      protected filterTechnicalRooms() {
        if (!this.technicalRooms) {
          return;
        }
        // get the search keyword
        let search = this.selectTechnicalRoomFilterCtrl.value;
        if (!search) {
          this.filteredTechnicalRooms.next(this.technicalRooms.slice());
          return;
        } else {
          search = search.toLowerCase();
        }
        // filter the technicalRooms
        this.filteredTechnicalRooms.next(
          this.technicalRooms.filter(technicalRoom => technicalRoom.nameRoom.toLowerCase().indexOf(search) > -1)
        );
      }

}

编辑:

我将 *ngIf="technicalRooms" 添加到 mat-form-field 上方的 div,但现在收到错误:

Cannot set property 'compareWith' of undefined

最佳答案

我按照[macjohnny] 1的指示成功解决了我的问题。 ,[GitHub 上的该问题。] 2

简而言之,问题是我需要先加载已过滤的列表,然后再将其设置在输入中。

问题中给出的解决方案是这样的:

@Input() set data(data: any[]) {
  this._data = data;
  // load the initial entity list
  this.filteredEntities.next(this.data.slice());
}
get data(): any[] {
  return this._data;
}
private _data: any[];

但首先,我根据问题作者的想法在 Angular 中创建了一个组件,每当我需要使用它时。我将此组件称为select-search

完整的解决方案如下所示:

select-search.component.html

<mat-form-field *ngIf="entityFilterCtrl">
    <mat-select [formControl]="entityCtrl" #singleSelect (selectionChange)="onChange($event)">
        <ngx-mat-select-search 
            [formControl]="entityFilterCtrl" 
            [placeholderLabel]="'Search'"
            [noEntriesFoundLabel]="'Not found'">
        </ngx-mat-select-search>
        <mat-option *ngFor="let entity of filteredEntities | async" [value]="entity.url">{{entity.name}}</mat-option>
    </mat-select>
</mat-form-field>

select-search.component.ts

import { Component, OnInit, ViewChild, Input, EventEmitter, Output } from '@angular/core';
import { MatSelect } from '@angular/material';
import { ReplaySubject, Subject } from 'rxjs';
import { FormControl } from '@angular/forms';
import { takeUntil, take } from 'rxjs/operators';

@Component({
  selector: 'app-select-search',
  templateUrl: './select-search.component.html',
  styleUrls: ['./select-search.component.css']
})
export class SelectSearchComponent implements OnInit {

  /** control for the selected entity */
  public entityCtrl: FormControl = new FormControl();

  /** control for the MatSelect filter keyword */
  public entityFilterCtrl: FormControl = new FormControl();

  /** list of entities filtered by search keyword */
  public filteredEntities: ReplaySubject<any[]> = new ReplaySubject<any[]>(1);

  @ViewChild('singleSelect', { static: true }) singleSelect: MatSelect;

  /** Subject that emits when the component has been destroyed. */
  private _onDestroy = new Subject<void>();

  @Input() set data(data: any[]) {
    this._data = data;
    // load the initial entity list
    this.filteredEntities.next(this.data.slice());
  }
  get data(): any[] {
    return this._data;
  }
  private _data: any[];

  @Output() onSelectionChange: EventEmitter<any> = new EventEmitter<any>();

  constructor() { }

  ngOnInit(): void {
    // listen for search field value changes
    this.entityFilterCtrl.valueChanges
      .pipe(takeUntil(this._onDestroy))
      .subscribe(() => {
        this.filterEntities();
      });

  }

  ngAfterViewInit(): void {
    this.setInitialValue();
  }

  ngOnDestroy(): void {
    this._onDestroy.next();
    this._onDestroy.complete();
  }

  onChange($event) {
    this.onSelectionChange.emit($event);
  }

  private setInitialValue() {
    this.filteredEntities
      .pipe(take(1), takeUntil(this._onDestroy))
      .subscribe(() => {
        // setting the compareWith property to a comparison function
        // triggers initializing the selection according to the initial value of
        // the form control (i.e. _initializeSelection())
        // this needs to be done after the filteredEntities are loaded initially
        // and after the mat-option elements are available
        if(this.singleSelect)
          this.singleSelect.compareWith = (a: any, b: any) => a.url === b.url;
      });
  }

  private filterEntities() {
    if (!this.data) {
      return;
    }
    // get the search keyword
    let search = this.entityFilterCtrl.value;
    if (!search) {
      this.filteredEntities.next(this.data.slice());
      return;
    } else {
      search = search.toLowerCase();
    }
    // filter the entitys
    this.filteredEntities.next(
      this.data.filter(entity => entity.name.toLowerCase().indexOf(search) > -1)
    );
  }

}

使用组件:

 <app-select-search class="selectList-full-width" [data]="pokemons"></app-select-search>

[我把它放在我的 GitHub 上作为示例] 3还有[我将其导入 StackBlitz,以便我可以看到它的工作] 4 .

关于angular - 为什么 ngx-mat-select-search 在此示例中不能处理动态数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60610349/

相关文章:

javascript - 如何以编程方式触发 Chowkidar 事件

javascript - 如何使用 Vue.js 将 TypeScript 文件编译为 JavaScript

angular - 类型 'nome' 上不存在属性 'FormGroup'

angular - 如何使用来自 Observable 的数据更新我的 Angular Table 数据源

angular - 在 ionic 3 应用程序中使用 JWT 进行身份验证

javascript - 在 Angular 中迭代 JSON 时出现问题

javascript - 如何检查组件子组件是否有子组件?

angular - 将类添加到自定义组件 Angular 2

angular - 未处理的 promise 拒绝 : Cannot match any routes

html - Angular 如何在 html 中显示数组中的单个对象