如何在 Angular 中打开自动完成时将选项滚动到视图中 8 - 可编辑下拉列表

How to scroll option into view on opening autocomplete in Angular 8 - Editable dropdown

我正在尝试创建一个行为类似于 select 但输入是可编辑的字段。要求是'The user should be able to select from options or type in a valid value'。选项不会被过滤,而是应该关注选项中的匹配值。我正在尝试使用 mat autocomplete 创建一个,但我无法修复滚动部分。在 selection 之后再次输入或聚焦字段时,如何将 selected 选项滚动到视图中?

HTML

<div>
  <mat-form-field [appearance]="'outline'">
    <mat-label>Select color</mat-label>
    <input type="text" matInput [(ngModel)]="color" [matAutocomplete]="colorOptions">
    <i class="icon-caret-down select-arrow" matSuffix></i>
    <mat-hint>Select or type a color</mat-hint>
  </mat-form-field>
  <mat-autocomplete #colorOptions="matAutocomplete">
    <mat-option *ngFor="let option of colors; let i=index" [value]="option"
      [ngClass]="{'active-option': option == color}">
      {{option}}
    </mat-option>
  </mat-autocomplete>
</div>

TS

public colors = ['Red', 'Green', 'Blue', 'Yellow', 'Orange', 'White', 'Black', 'Purple', 'Grey', 'Brown'];
public color = '';

SCSS

.active-option {
  background-color: #f5f5f5 !important;
  font-weight: bold !important;
}

https://stackblitz.com/edit/angular-ivy-vetnpq

如果我理解你想要的是将用户键入的选项添加到 mat-autocomplete 的选项中。如果我明白您的意思,请尝试查看以下代码的 StackBlitz 编辑 edited-stackblitz

我添加了一个按钮,当用户按下该按钮时,我将选项添加到自动完成列表中。您还可以在用户按下“enter”并触发我添加到按钮的操作时进行拦截。

这里是编辑后的代码:
HTML:

   <div [formGroup]="testForm">
     <mat-form-field [appearance]="'outline'">
       <mat-label>Select color</mat-label>
       <input type="text" matInput formControlName="color" 
             [matAutocomplete]="colorOptions" [(ngModel)]="currentOption">
       <i class="icon-caret-down select-arrow" matSuffix></i>
       <mat-hint>Select or type a color</mat-hint>
     </mat-form-field>
     <mat-autocomplete #colorOptions="matAutocomplete">
       <mat-option *ngFor="let option of colors; let i=index" [value]="option"
         [ngClass]="{'active-option': option == testForm.controls.color.value}">
         {{option}}
       </mat-option>
     </mat-autocomplete>
     <button (click)="addOption()">+</button>
     {{currentOption}}
    </div>

TS:

  name = 'Angular ' + VERSION.major;
  color = '';
  colors = ['Red', 'Green', 'Blue', 'Yellow', 'Orange', 'White', 'Black', 'Purple', 'Grey', 'Brown'];
  testForm: FormGroup;

  ngOnInit(){
    this.testForm = new FormGroup({
      color: new FormControl('')
    })
  }

  public currentOption: any;

  public addOption(): void {
    this.colors.push(this.currentOption);
  }

让我知道我是否理解正确。

好的,我们开始....

让我们从一点开始 StackBlitz demo

请注意,当有人使用输入中的 keydown 时,您还应该关注所选元素,我没有实现,因为它有点超出问题的范围。总的来说,我建议使用默认行为,但既然你问了..

css

在html中有一个div你可以通过它的role='listbox'来识别。此 div 包含 mat-option 个元素。当 mat-option 元素不适合 div 时,div 将添加带有 overflow: auto 的滚动条。所以我们只需要在 div 上设置 scrollTop 值即可滚动。

如何获取元素

通过名为 panel 的自动完成对象的 属性 获取 div。为此,获取自动完成对象并使用 @ViewChild().

引用它

计算要在scrollTop上设置的值

要计算值,请获取 mat-option 的高度。默认值为 48,因此您可以直接设置它。您应该能够从 AUTOCOMPLETE_OPTION_HEIGHT.

中获取高度

注意:我无法通过修改此值获得可见的结果。也许我做错了什么。或者可能有什么原因导致修改这个常量没有实际效果。所以我直接设置为默认值48.

要获得正确的 scrollTop 值,请使用匹配元素的索引计算它。

通过方法调用添加逻辑

使用检测输入值变化的方法调用此逻辑:(input)='changed_input()

下面是我的代码

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import {AUTOCOMPLETE_OPTION_HEIGHT} from '@angular/material/autocomplete';

@NgModule({
  imports:      [ 
    BrowserModule, 
    BrowserAnimationsModule,
    FormsModule,
    ReactiveFormsModule,
    MatFormFieldModule,
    MatInputModule,
    MatAutocompleteModule
  ],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ],
  providers: [
    {provide: AUTOCOMPLETE_OPTION_HEIGHT, useValue: 48 }
  ]
})
export class AppModule { }

app.component.html

<div [formGroup]="testForm">
  <mat-form-field [appearance]="'outline'">
    <mat-label>Select color</mat-label>
    <input type="text" matInput [(ngModel)]="color"
    (input)='changed_input()'formControlName="color" [matAutocomplete]="colorOptions">
    <i class="icon-caret-down select-arrow" matSuffix></i>
    <mat-hint>Select or type a color</mat-hint>
  </mat-form-field>
  <mat-autocomplete 
  #matAutocomplete #colorOptions="matAutocomplete">
    <mat-option *ngFor="let option of colors; let i=index" [value]="option">
      {{option}}
    </mat-option>
  </mat-autocomplete>
</div>

app.component.ts

import { Component, VERSION, ViewChild, Inject } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import {AUTOCOMPLETE_OPTION_HEIGHT} from '@angular/material/autocomplete';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  @ViewChild('matAutocomplete') matAutocomplete;
  color = '';
  colors = ['Red', 'Green', 'Blue', 'Yellow', 'Orange', 'White', 'Black', 'Purple', 'Grey', 'Brown'];
  testForm: FormGroup;

  constructor(
    @Inject(AUTOCOMPLETE_OPTION_HEIGHT) public option_height: number
  ) {}
  ngOnInit(){
    this.testForm = new FormGroup({
      color: new FormControl('')
    })
  }
  public changed_input(): void {
    const color_index = this.colors.findIndex( color_option => {
      return color_option.toLowerCase() === this.color.toLowerCase();
    });
    if(color_index === -1 ) return;
    this.matAutocomplete.panel.nativeElement.scrollTop = this.option_height*color_index;
  }
}

最后的说明

这一切都很有趣和咯咯笑,但认真地使用默认行为并为你未来的自己省去一些痛苦。