javascript - 垫自动完成功能未正确过滤 Angular

标签 javascript html css angular angular-material

我正在自定义 Angular Material 选择/自动完成以允许嵌套下拉菜单。

在这里,我尝试搜索父值或子值,但它没有被过滤掉。

此外,它应该像阿拉斯加(+2 个其他)一样显示。

STACKBLITZ

<mat-form-field appearance="fill">
  <mat-label>Toppings</mat-label>
    <!-- <input type="text" matInput placeholder="States Group" formControlName="stateGroup" required [matAutocomplete]="autoGroup">
     <mat-autocomplete #autoGroup="matAutocomplete"> -->
           <input type="text" matInput placeholder="Select Users" aria-label="Select Users" matInput [matAutocomplete]="auto" [formControl]="states">
<mat-autocomplete #auto="matAutocomplete">

  <!-- <mat-select [formControl]="states" multiple> -->
    <mat-select-trigger>
      {{states.value ? states.value[0] : ''}}
      <span *ngIf="states.value?.length > 1" class="example-additional-selection">
        (+{{states.value.length - 1}} {{states.value?.length === 2 ? 'other' : 'others'}})
      </span>
    </mat-select-trigger>

  <mat-optgroup *ngFor="let group of stateList">
          <div>
          <mat-checkbox [checked]="group.selected" (change)="toggleParent($event, group)"
            (click)="$event.stopPropagation()">
                    {{group.letter}}
                </mat-checkbox>
          <button mat-button (click)="expandDocumentTypes(group)">
            <mat-icon>keyboard_arrow_down</mat-icon>
          </button>
          </div>  
          <mat-option *ngFor="let name of group.names" [value]="name"
             [ngClass]="isExpandCategory[group.letter] ? 'list-show' : 'list-hide'">
             <mat-checkbox [checked]="group.checked" (change)="toggleSelection($event, name, group)" 
            (click)="$event.stopPropagation()">
                    {{name.type}}
                </mat-checkbox>
          </mat-option>
  </mat-optgroup>

  <!-- </mat-select> -->
</mat-autocomplete>
     <!-- </mat-autocomplete> -->
</mat-form-field>


export class SelectCustomTriggerExample {
  constructor(private _formBuilder: FormBuilder){}

  // stateForm: FormGroup = this._formBuilder.group({
  //   stateGroup: '',
  // });
  // toppings = new FormControl();
  isExpandCategory: boolean[] = [];
  toppingList: string[] = ['Extra cheese', 'Mushroom', 'Onion', 'Pepperoni', 'Sausage', 'Tomato'];
  stateRecord:any = [];
  states = new FormControl();

  expandDocumentTypes(group: any) {
    console.log("expanding dropdown", group);
    this.isExpandCategory[group.letter] = !this.isExpandCategory[group.letter];
    // expand only selected parent dropdown category with that childs
  }

toggleSelection(event:any, name: any, group: any) {
  debugger;
  console.log("toggleSelection", name, event.checked, group);
  if(event.checked) {
    console.log("stastateRecordtelist", this.stateRecord);
    this.stateRecord.push(name.type);
    this.states.setValue(this.stateRecord);
    console.log("toggleselection ", this.states.value);
  }
  else {
      this.stateRecord = this.stateRecord.filter((x:any) => x!== name.type);
      console.log("else toggleselection", name, group, this.states.value);
      this.states.setValue(this.states.value.filter((x:any) => x!== name.type));
      console.log("after filter ", this.states.value);
      //this.states.setValue([]);
  }
}

 toggleParent(event: any, group: any) {
    debugger;
    group.checked = event.checked;
    console.log("event", event.checked, "group", group, "states value", this.states.value);
    let states = this.states.value;
    states = states ? states : [];
    if(event.checked) {
      states.push(...group.names.filter((x: any) => !states.includes(x.type)).map((x: any) => x.type))
    } else {
      console.log("else", states);
      group.names.forEach((x: any) => {
          if(states.indexOf(x.type) > -1) {
            states.splice(states.indexOf(x.type), 1)
          }
        });
    }
    this.states.setValue(states);
    console.log("statesvalue", this.states.value);
      if(!event.checked) {
          this.states.setValue(this.states.value.filter((x:any) => !x.includes(group.names)))
        //this.states.setValue([]);
    }
    console.log("final statesvalue", this.states.value);
    this.stateRecord = this.states.value;
  }

  stateList = [
   {
      "letter":"A",
      "checked":false,
      "names":[
         {
            "id":1,
            "type":"Alabama"
         },
         {
            "id":2,
            "type":"Alaska"
         },
         {
            "id":3,
            "type":"Arizona"
         },
         {
            "id":4,
            "type":"Arkansas"
         }
      ]
   },
   {
      "letter":"C",
      "checked":false,
      "names":[
         {
            "id":8,
            "type":"California"
         },
         {
            "id":9,
            "type":"Colorado"
         },
         {
            "id":10,
            "type":"Connecticut"
         }
      ]
   },
   {
      "letter":"D",
      "checked":false,
      "names":[
         {
            "id":18,
            "type":"Delaware"
         },
         {
            "id":19,
            "type":"Denwer"
         }
      ]
   }
];
}

引用文献:

https://stackblitz.com/edit/angular-evacck-qubgyy

https://stackblitz.com/angular/eboprqqnooy

有人可以帮忙吗?

最佳答案

这个任务非常复杂,需要更多的澄清,也许可以通过一些专用的库来解决,比如 https://www.npmjs.com/package/mat-select-autocomplete

但我喜欢您逐步推进此自定义控件,并且希望获得比已实现的更好的东西。

让我们尝试解决您在此问题中指出的两个问题:

它应该过滤父值或子值

Angular Material 使我们能够 define custom filter 。您可以在示例中使用类似的逻辑。只需创建一个 Observable 来监听 FormControl 更改:

this.stateGroupOptions$ = this.states.valueChanges.pipe(
      startWith(EMPTY_STRING),
      ...
      map(value => this._filterGroup(value))
    );
...

private _filterGroup(value: any) {
  if (!this.searching || !value.trim()) {
    return this.stateList;
  }

  value = value.trim().toLowerCase();

  return this.stateList
    .map(group => ({
      ...group,
      names: group.names.filter(item => item.type.toLowerCase().includes(value))
    }))
    .filter(
      group => group.letter.toLowerCase().includes(value) || group.names.length > 0
    );
}

然后在选项列表中使用它

<mat-optgroup *ngFor="let group of stateGroupOptions$ | async">

它应该显示为阿拉斯加(+2 个其他)

同样,这种情况有专用的功能 - 显示 input property

@Input() displayWith: ((value: any) => string) | null Function that maps an option's control value to its display value in the trigger.

html

<mat-autocomplete ... [displayWith]="displayWith"

ts

displayWith(names: StateName[]) {
  if (!names || !names.length) {
    return "";
  }
  return (
    names[0].type +
    (names.length > 1
      ? ` (+${names.length - 1} ${names.length === 2 ? "other" : "others"})`
      : "")
  );
}

<强> Stackblitz Example

此自定义实现的主要技巧是在不同类型的数据之间保持模型一致:字符串(当用户开始输入时),数组(当您单击复选框时)并手动更新控件)和对象(当您从下拉列表中选择选项时)。

注意: 您仍然可以对其进行优化,并且可能还有很多问题您尚未发现

<强> Latest version

关于javascript - 垫自动完成功能未正确过滤 Angular ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63173753/

相关文章:

javascript - 删除 Firebase 的任何用户帐户/电子邮件地址

javascript - Jquery Accordion 自动关闭

javascript - Div 标签背景颜色 CSS HTML

javascript - 在javascript中每隔一个逗号拆分

javascript - 单击时将来自 img 的 ID 插入到输入中

javascript - 如何将 chart.js 图表保存在一个 JS 文件中,并且当 JS 文件中的 ID 在一个特定的 html 页面上不存在时不会出现错误?

php - Jquery 在 PHP 中切换加号/减号或向上/向下滑动

jquery - 具有不同的子菜单字体和一个简单的 jquery 垂直 Accordion 菜单

javascript - 具有延迟调整的 onclick 切换类

javascript - 如何从数组创建数组