How do i implement autocomplete in a component?

后端 未结 3 1561
甜味超标
甜味超标 2020-12-16 00:37

I would like to combine the autocomplete feature with a multiselect mat-select chooser because the optionslist will be quite long.

I\'ve already searched on stackove

相关标签:
3条回答
  • 2020-12-16 01:03

    You can implement an autocomplete multi-select using MatAutocomplete and some tricks. I'm fairly sure you can't do it with MatSelect since that doesn't give you control over the input element. The technique is a little odd, but it works fine. The idea is to manage selections in your controller, and set the "value" of each mat-option to the same thing - your selected options. You also need to trap clicks to allow users to interact with the list (rather than have it close immediately when clicked), and of course provide checkboxes inside the mat-option items. A few other tricks are necessary as well - here is a quick and dirty example that shows what to do.

    HTML:

    <mat-form-field class="example-full-width">
        <input type="text" placeholder="Select Users" aria-label="Select Users" matInput [matAutocomplete]="auto" [formControl]="userControl">
        <mat-hint>Enter text to find users by name</mat-hint>
    </mat-form-field>
    
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
        <mat-option *ngFor="let user of filteredUsers | async" [value]="selectedUsers">
            <div (click)="optionClicked($event, user)">
                <mat-checkbox [checked]="user.selected" (change)="toggleSelection(user)" (click)="$event.stopPropagation()">
                    {{ user.firstname }} {{ user.lastname }}
                </mat-checkbox>
            </div>
        </mat-option>
    </mat-autocomplete>
    
    <br><br>
    
    <label>Selected Users:</label>
    <mat-list dense>
        <mat-list-item *ngIf="selectedUsers?.length === 0">(None)</mat-list-item>
        <mat-list-item *ngFor="let user of selectedUsers">
            {{ user.firstname }} {{ user.lastname }}
        </mat-list-item>
    </mat-list>
    

    TS:

    import { Component, OnInit, ViewChild } from '@angular/core';
    import { FormControl } from '@angular/forms';
    import { Observable } from 'rxjs';
    import { map, startWith } from 'rxjs/operators';
    
    export class User {
      constructor(public firstname: string, public lastname: string, public selected?: boolean) {
        if (selected === undefined) selected = false;
      }
    }
    
    /**
     * @title Multi-select autocomplete
     */
    @Component({
      selector: 'multiselect-autocomplete-example',
      templateUrl: 'multiselect-autocomplete-example.html',
      styleUrls: ['multiselect-autocomplete-example.css']
    })
    export class MultiselectAutocompleteExample implements OnInit {
    
      userControl = new FormControl();
    
      users = [
        new User('Misha', 'Arnold'),
        new User('Felix', 'Godines'),
        new User('Odessa', 'Thorton'),
        new User('Julianne', 'Gills'),
        new User('Virgil', 'Hommel'),
        new User('Justa', 'Betts'),
        new User('Keely', 'Millington'),
        new User('Blanca', 'Winzer'),
        new User('Alejandrina', 'Pallas'),
        new User('Rosy', 'Tippins'),
        new User('Winona', 'Kerrick'),
        new User('Reynaldo', 'Orchard'),
        new User('Shawn', 'Counce'),
        new User('Shemeka', 'Wittner'),
        new User('Sheila', 'Sak'),
        new User('Zola', 'Rodas'),
        new User('Dena', 'Heilman'),
        new User('Concepcion', 'Pickrell'),
        new User('Marylynn', 'Berthiaume'),
        new User('Howard', 'Lipton'),
        new User('Maxine', 'Amon'),
        new User('Iliana', 'Steck'),
        new User('Laverna', 'Cessna'),
        new User('Brittany', 'Rosch'),
        new User('Esteban', 'Ohlinger'),
        new User('Myron', 'Cotner'),
        new User('Geri', 'Donner'),
        new User('Minna', 'Ryckman'),
        new User('Yi', 'Grieco'),
        new User('Lloyd', 'Sneed'),
        new User('Marquis', 'Willmon'),
        new User('Lupita', 'Mattern'),
        new User('Fernande', 'Shirk'),
        new User('Eloise', 'Mccaffrey'),
        new User('Abram', 'Hatter'),
        new User('Karisa', 'Milera'),
        new User('Bailey', 'Eno'),
        new User('Juliane', 'Sinclair'),
        new User('Giselle', 'Labuda'),
        new User('Chelsie', 'Hy'),
        new User('Catina', 'Wohlers'),
        new User('Edris', 'Liberto'),
        new User('Harry', 'Dossett'),
        new User('Yasmin', 'Bohl'),
        new User('Cheyenne', 'Ostlund'),
        new User('Joannie', 'Greenley'),
        new User('Sherril', 'Colin'),
        new User('Mariann', 'Frasca'),
        new User('Sena', 'Henningsen'),
        new User('Cami', 'Ringo')
      ];
    
      selectedUsers: User[] = new Array<User>();
    
      filteredUsers: Observable<User[]>;
      lastFilter: string = '';
    
      ngOnInit() {
        this.filteredUsers = this.userControl.valueChanges.pipe(
          startWith<string | User[]>(''),
          map(value => typeof value === 'string' ? value : this.lastFilter),
          map(filter => this.filter(filter))
        );
      }
    
      filter(filter: string): User[] {
        this.lastFilter = filter;
        if (filter) {
          return this.users.filter(option => {
            return option.firstname.toLowerCase().indexOf(filter.toLowerCase()) >= 0
              || option.lastname.toLowerCase().indexOf(filter.toLowerCase()) >= 0;
          })
        } else {
          return this.users.slice();
        }
      }
    
      displayFn(value: User[] | string): string | undefined {
        let displayValue: string;
        if (Array.isArray(value)) {
          value.forEach((user, index) => {
            if (index === 0) {
              displayValue = user.firstname + ' ' + user.lastname;
            } else {
              displayValue += ', ' + user.firstname + ' ' + user.lastname;
            }
          });
        } else {
          displayValue = value;
        }
        return displayValue;
      }
    
      optionClicked(event: Event, user: User) {
        event.stopPropagation();
        this.toggleSelection(user);
      }
    
      toggleSelection(user: User) {
        user.selected = !user.selected;
        if (user.selected) {
          this.selectedUsers.push(user);
        } else {
          const i = this.selectedUsers.findIndex(value => value.firstname === user.firstname && value.lastname === user.lastname);
          this.selectedUsers.splice(i, 1);
        }
    
        this.userControl.setValue(this.selectedUsers);
      }
    
    }
    
    0 讨论(0)
  • 2020-12-16 01:06

    One way is to add a manual filter inside mat-select so it will have auto-complete functionality. Find the example code of below given solution here

    add a input controller for filter text

        public filterControl = new FormControl();
    

    add the filter text field inside mat-select

         <mat-select
            [id]="fieldId"
            [formControl]="custonDropdown"
            (selectionChange)="onSelectChange($event.value)"
            (closed)="onPanelClose()"
            multiple
            panelClass="custom-mat-select">
            <input matInput 
                 [formControl]="filterControl" 
                 type="text" 
                 name="filter-options" 
                 id="filter-options" 
                 placeholder="Search">
            <mat-option *ngFor="let option of filteredOptions | async"
                 [value]="option.value" 
                 [ngClass]="{'hide': !option.show}">
                {{option.name | translate}}
            </mat-option>
        </mat-select>
    

    now add a event listener for filter text field value change

         this.filteredOptions = this.filterControl.valueChanges.pipe(
                startWith(''),
                map((value: string) => {
                    this.optionItems
                        .forEach(option => {
                            option.show = option.name.toLocaleLowerCase().includes(value.toLowerCase());
                        });
                    return this.optionItems;
                })
            );
    

    also to make the whole list to be view in next dropdown open clear the filter text field on panel close, as mentioned in the template code (closed)="onPanelClose()"

     onPanelClose() {
        this.filterControl.setValue('');
    }
    

    Demo

    0 讨论(0)
  • 2020-12-16 01:18

    Yes you can use PrimeNG multiselect control. Only issue with that is if you are worried about themes.

    There is another option using mat-select-autocomplet

    How to install:

    npm install select-autocomplete --save
    

    npm: https://www.npmjs.com/package/mat-select-autocomplete

    Demo: https://stackblitz.com/edit/mat-select-autocomplete

    0 讨论(0)
提交回复
热议问题