Search code examples
javascriptangulartypescriptangular-cdk

Angular ul, cdk-virtual-scroll-viewport list, keyboard selection


Trying to create an Autocomplete component functionality.

When a user starts typing, a small window pops up with options to select from. I would like the user to be able to pick an option using his keyboard.

Example: - User typing "Hou" - Options show ["House", "Houston"] - User hits down keyboard key and hits enter to select "house"

My pop up scroll component:

<ul class="plDropdown-optionList">
          <cdk-virtual-scroll-viewport style="height: 200px" itemSize="30">
            <ng-container *cdkVirtualFor="let option of displayableOptions">
              <button class="plDropdown-option" type="button" (click)="selectOption(option)">
                {{option}}
              </button>
            </ng-container>
          </cdk-virtual-scroll-viewport>
        </ul>

Solution

  • Posting a solution option for anyone looking for this.

    I used keyboard event from Input tag:

    <input (keydown)="processInput($event)">
    

    Then finding which was pressed:

    processInput(evt: KeyboardEvent): void
    

    Keeping selected reference, and updating it:

    private _updateIndex(key: string): void {
        switch (key) {
          case Key.ArrowUp:
            if (this.selectedIndex === 0) {
              return;
            }
    
            this._setIndex(this.selectedIndex -= 1);
            break;
    
          case Key.ArrowDown:
            this.displayedTags$
            .pipe(
              take(1)
            )
              .subscribe((results) => {
                if (this.selectedIndex >= results.length - 1) {
                  this._setIndex(results.length - 1);
    
                  return;
                }
    
                this._setIndex(this.selectedIndex += 1);
              });
            break;
    
          default:
            break;
        }
      }
    
      private _selectIndex(): void {
        this.displayedTags$.pipe(
          take(1)
        )
        .subscribe((results) => {
          const result = results[this.selectedIndex];
    
          if (result) {
            this.selectResult(result);
          }
        });
      }
    
      private _setIndex(index: number): void {
        this.selectedIndex = index;
      }
    

    At HTML file, use this line for highlight the currect line:

    [ngClass]="{'focus-background': selectedIndex == idx}"
    

    it looks like this:

     <cdk-virtual-scroll-viewport style="height: 400px" itemSize="30">
                <ng-container *cdkVirtualFor="let option of displayedTags$ | async; index as idx" (isSelected)="true">
                  <button class="plTagsComponent-option" type="button" (click)="selectResult(option)" (isSelected)="true"
                    [ngClass]="{'focus-background': selectedIndex == idx}" (mouseenter)="onHover(idx) ">
                    {{option.label}}
                  </button>
                </ng-container>
              </cdk-virtual-scroll-viewport>