代码之家  ›  专栏  ›  技术社区  ›  Ross Graeber

如何使用角度材质自动完成复杂对象?

  •  0
  • Ross Graeber  · 技术社区  · 2 年前

    我正在构建一个在Angular 16中使用Angular Material的应用程序。我有ngrx状态管理,它收集了一组可供选择的对象。当从自动完成选项中选择一个对象时,我希望输入显示该对象的名称属性。我目前正在使用matInput和mat自动完成功能。

    import { Component, OnInit } from '@angular/core';
    import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
    import {AsyncPipe} from '@angular/common';
    import {MatAutocompleteModule} from '@angular/material/autocomplete';
    import {MatInputModule} from '@angular/material/input';
    import {MatFormFieldModule} from '@angular/material/form-field';
    import { TokenNameWithId } from '../../../shared/models/card.model';
    import { Observable, combineLatest, debounceTime, map, startWith } from 'rxjs';
    import { Store } from '@ngrx/store';
    import { selectAllTokenNames } from '../+state/token-names.selectors';
    
    @Component({
      selector: 'tokenator-tokens-dropdown',
      standalone: true,
      imports: [FormsModule,
        MatFormFieldModule,
        MatInputModule,
        MatAutocompleteModule,
        ReactiveFormsModule,
        AsyncPipe
      ],
      providers: [
      ],
      templateUrl: './tokens-dropdown.component.html',
      styleUrl: './tokens-dropdown.component.scss',
    })
    export class TokensDropdownComponent implements OnInit {
      public tokenNamesControl = new FormControl<string>('', {
        nonNullable: true,
      });
      public filteredTokenNames!: Observable<TokenNameWithId[]>;
      private tokenNames$ = this.store.select(selectAllTokenNames);
    
      constructor(private store: Store) {
      }
    
      ngOnInit(): void {
        this.filteredTokenNames = combineLatest([
          this.tokenNamesControl.valueChanges.pipe(startWith('')),
          this.tokenNames$,
        ]).pipe(
          debounceTime(250),
          map(([value, tokenNamesWithId]) => value ? this._filter(value, tokenNamesWithId) : tokenNamesWithId
        ));
      }
    
      private _filter(value: string, tokenNamesWithId: TokenNameWithId[]): TokenNameWithId[] {
        const filterValue = value.toLowerCase();
        return tokenNamesWithId.filter((tokenNameWithId: TokenNameWithId) => tokenNameWithId.displayName.includes(filterValue));
      }
    }
    .example-form {
        min-width: 150px;
        max-width: 500px;
        width: 100%;
      }
      
      .example-full-width {
        padding-top: 10px;
        width: 100%;
      }
    <form class="example-form">
        <mat-form-field class="example-full-width">
            <mat-label>Token</mat-label>
            <input 
                type="text"
                placeholder="Select a token"
                aria-label="Token name"
                matInput
                [formControl]="tokenNamesControl"
                [matAutocomplete]="auto">
            <mat-autocomplete #auto="matAutocomplete">
                @for (tokenName of filteredTokenNames | async ; track tokenName) {
                    <mat-option [value]="tokenName">{{tokenName.displayName}}</mat-option>
                }
            </mat-autocomplete>
        </mat-form-field>
    </form>

    这导致matInput的奇怪行为,在自动完成中搜索工作的地方,我可以选择一个对象,但显示的值是 [object Object] 。这是有道理的,因为从自动完成中选择的基本值是一个对象,但我希望输入显示的文本为 tokenName.displayName 就像我的 <mat-options>

    This is a display of the form input as displayed on the web page after selecting an available option from autocomplete.  The list of autocomplete options displays the proper text but the input selected value is "object Object".

    有没有办法拿到 matInput 显示从自动完成中选择的对象的属性?或者,当我从阵列中选择更复杂的对象时,是否有更好的角度材料组件配对,以保持工作自动完成?

    1 回复  |  直到 2 年前
        1
  •  1
  •   Yong Shun    2 年前

    您需要使用 [displayWith] 属性自定义所选选项的显示文本。

    displayFn(tokenName: any): string {
      return tokenName && tokenName.displayName ? tokenName.displayName : '';
    }
    
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
        ...
    </mat-autocomplete>
    

    参考 Setting separate control and display values

    对于您报告的问题:

    1. 更改 FormControl<string> FormControl<string | TokenNameWithId> 类型

    2. 修改 _filter 检查的函数 value 类型

    public tokenNamesControl = new FormControl<string | TokenNameWithId>('', {
        nonNullable: true,
      });
    
    private _filter(value: string | TokenNameWithId, tokenNamesWithId: TokenNameWithId[]): any[] {
      const filterValue =
        typeof value === 'string'
          ? value.toLowerCase()
          : value.displayName.toLowerCase();
    
      return tokenNamesWithId.filter((tokenNameWithId: any) =>
        tokenNameWithId.displayName.toLowerCase().includes(filterValue)
      );
    }
    
    推荐文章