Имеется ли раскрывающийся список с функцией фильтра в материале angular? NB: использование mat-select, а не mat-option - PullRequest
0 голосов
/ 14 февраля 2020

Я искал опцию отфильтрованного раскрывающегося списка в материале angular и не смог найти что-либо с множественным выбором mat-select. Я не думаю, что реализация доступна в angular материале для mat-select. Есть ли способ реализовать это, используя angular материал?

Ответы [ 2 ]

0 голосов
/ 16 февраля 2020

Ну, мы можем создать элемент управления вводом материала, который был множественным выбором с фильтром.

Поскольку ответ немного больше, вы можете увидеть результат в стекблиц

Идея состоит в том, что у нас есть компонент с @input, который может быть массивом строк или массивом объектов. У нас есть три вспомогательные переменные

  _list: any[]; //<--an array of object
  keys: string[]; //an array with two values, the "key" and the "text"
  listFiltered: any[]; //<--the list above filtered

и два formControls, один для отображения значения и один для фильтрации списка

  control = new FormControl();
  search = new FormControl();

Когда мы получили список во входе, мы создаем _list и присваиваем значение ключам

  @Input() set list(value) {
    this._list =
      typeof value[0] == "string"
        ? value.map(x => ({ key: x, value: x }))
        : [...value];
    this.keys = Object.keys(this._list[0]);

Так, например,

list: any[] = [
    {id:1,name:"Extra cheese"},
    {id:2,name:"Mushroom"},
    {id:3,name:"Onion"},
    {id:4,name:"Pepperoni"},
    {id:5,name:"Sausage"},
    {id:6,name:"Tomato"}
  ];
_list=[...list]
keys[0]="id"; keys[1]="name"

if

list=["Extra cheese","Mushroom","Onion","Pepperoni",name:"Sausage","Tomato"}
_list will be
        {key:"Extra cheese",value:"Extra cheese"},
        {key:"Mushroom",value:"Mushroom"},
        {key:"Onion",value:"Onion"},
        {key:"Pepperoni",value:"Pepperoni"},
        {key:"Sausage",value:"Sausage"},
        {key:"Tomato",value:"Tomato"}
 and 
    keys[0]="key"; keys[1]="value"

Это позволяет нам создать компонент, который имеет formControl и меню " "

<div class="multi-select">
      <input (click)="trigger.openMenu()" readonly [formControl]="control" />
      <span #menu class="mat-select-wrapper" [matMenuTriggerFor]="appMenu" (menuOpened)="searchID.focus()">
        <span class="mat-select-arrow"> </span>
      </span>
    </div>
    <mat-menu #appMenu="matMenu" xPosition="before">
      <div class="menu" (click)="$event.stopPropagation()">
        <mat-form-field>
          <mat-label>Search</mat-label>
          <input #searchID matInput placeholder="search" [formControl]="search" />
        </mat-form-field>
        <div class="mat-menu-item" *ngFor="let item of listFiltered">
          <mat-checkbox
            [checked]="item.checked"
            (change)="change($event.checked, item[keys[0]])"
          >
            {{ item[keys[1]] }}</mat-checkbox
          >
        </div>
      </div>
    </mat-menu>

Нам нужно использовать ViewChild, чтобы открыть меню, когда элемент управления сфокусирован

@ViewChild(MatMenuTrigger, { static: false }) trigger: MatMenuTrigger;

Если мы используем в ngOnInit

    this.search.valueChanges
      .pipe(
        startWith(null),
        delay(200)
      )
      .subscribe(res => {
        const search = res ? res.toLowerCase() : "";
        this.listFiltered = this._list.filter(
          x => !res || x.checked ||
                x[this.keys[1]].toLowerCase().indexOf(search) >= 0
        );
      });
  }

An функцию (изменить)

change(value, key) {
    const item = this._list.find(x => x[this.keys[0]] == key);
    item.checked = value;
  }

когда мы изменим поиск, listFiltered содержит элементы элемента управления и элементы, содержащие значение, а this._list будет массивом с элементами, у которых свойство "проверено" "Это становится правдой, если выбрано.

Что ж, теперь сложная часть - это преобразование в специальный пользовательский элемент управления. Нам нужно следовать руководству в документации

Вкратце, нам нужно добавить провайдера и разместить несколько классов

  providers: [
    { provide: MatFormFieldControl, useExisting: MultiSelectFilterComponent }
  ],
  host: {
    "[class.example-floating]": "shouldLabelFloat",
    "[id]": "id",
    "[attr.aria-describedby]": "describedBy"
  }

В конструкторе ввести FocusMonitor, ElementRef и ngControl

  constructor(
    private _focusMonitor: FocusMonitor,
    private _elementRef: ElementRef<HTMLElement>,
    @Optional() @Self() public ngControl: NgControl
  ) {
    _focusMonitor.monitor(_elementRef, true).subscribe(origin => {
      if (this.focused && !origin) {
        this.onTouched();
      }
      this.focused = !!origin;
      this.stateChanges.next();
    });

    if (this.ngControl != null) {
      this.ngControl.valueAccessor = this;
    }
  }

Добавьте некоторые переменные и входы

  controlType = "multi-select-filter";
  static nextId = 0;
  static ngAcceptInputType_disabled: boolean | string | null | undefined;
  id = `multi-select-filter-${MultiSelectFilterComponent.nextId++}`;
  describedBy = "";
  onChange = (_: any) => {};
  onTouched = () => {};

  stateChanges = new Subject<void>();
  focused = false;
  get errorState() //<----This is IMPORTANT, give us if the control is valid or nor
  {
      return this.ngControl?this.ngControl.invalid && this.ngControl.touched:false;
  } 
  get empty() {
    return !this.control.value;
  }
  get shouldLabelFloat() {
    return this.focused || !this.empty;
  }
  @Input()
  get placeholder(): string {
    return this._placeholder;
  }
  set placeholder(value: string) {
    this._placeholder = value;
    this.stateChanges.next();
  }
  private _placeholder: string;

  @Input()
  get required(): boolean {
    return this._required;
  }
  set required(value: boolean) {
    this._required = coerceBooleanProperty(value);
    this.stateChanges.next();
  }
  private _required = false;

  @Input()
  get disabled(): boolean {
    return this._disabled;
  }
  set disabled(value: boolean) {
    this._disabled = coerceBooleanProperty(value);
    this._disabled ? this.control.disable() : this.control.enable();
    this.stateChanges.next();
  }
  private _disabled = false;

  @Input()
  get value(): any[] | null {  //<--this is the value of our control
    if (!this._list) return null; //In my case we return an array based in 
                                  //this._list
    const result = this._list.filter((x: any) => x.checked);
    return result && result.length > 0
      ? result.filter(x => x.checked).map(x => x[this.keys[0]])
      : null;
  }
  set value(value: any[] | null) {
    if (this._list && value) {
      this._list.forEach(x => {
        x.checked = value.indexOf(x[this.keys[0]]) >= 0;
      })
        const result = this._list.filter((x: any) => x.checked);
        this.control.setValue(
          result.map((x: any) => x[this.keys[1]]).join(",")
        );
        this.onChange(result.map((x: any) => x[this.keys[0]]));
    } else 
    {
      this.onChange(null);
        this.control.setValue(null);
    }
    this.stateChanges.next();
  }

И методы

  ngOnDestroy() {
    this.stateChanges.complete();
    this._focusMonitor.stopMonitoring(this._elementRef);
  }

  setDescribedByIds(ids: string[]) {
    this.describedBy = ids.join(" ");
  }

  onContainerClick(event: MouseEvent) {
    if ((event.target as Element).tagName.toLowerCase() != "input") {
      this._elementRef.nativeElement.querySelector("input")!.focus();
    }
  }
  writeValue(value: any[] | null): void {
    this._value = value;
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }
0 голосов
/ 14 февраля 2020

Нет компонента по умолчанию в Angular Материал, который имеет раскрывающийся список и фильтр, встроенный в один компонент. Но есть три разных способа использования выпадающего меню с фильтром.

  1. Angular Материал автозаполнение
  2. Создание собственного выпадающего меню с фильтром с помощью mat -выберите и введите, как показано здесь

  3. Используйте сторонние компоненты, которые предоставляют такие же функциональные возможности, как mat-select-search

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...