选择下拉选项

时间:2017-12-06 21:38:06

标签: angular angular-material observable

我随时从Material Autocomplete中选择一个选项,它会对服务器进行额外的HTTP GET调用。选择下拉选项的理想结果应该是填充输入。

从服务器动态检索下拉选项。

HTML

<form class="example-form">
    <mat-form-field class="example-full-width">
      <input type="text" placeholder="search item" aria-label="Item1" matInput [formControl]="searchTerm" [matAutocomplete]="auto1">
      <mat-autocomplete #auto1="matAutocomplete" [displayWith]="displayFn">
        <mat-option *ngFor="let item of searchResult" [value]="item.Id">
          {{ item.Name }}
        </mat-option>
      </mat-autocomplete>
    </mat-form-field>
  </form>

打字稿

    searchTerm : FormControl = new FormControl();

    searchResult = [];
    constructor(private service: AppService){
      this.searchTerm.valueChanges
          .debounceTime(400) 
          .subscribe(data => {
              this.service.search_word(data).subscribe(response =>{
                  this.searchResult = response
              })

其他一切都很好。唯一的问题是当选择自动完成选项时意外额外调用服务器。

快速更新:没有答案完全解决了这个问题。但是,我把它缩小到可能是displayWith的问题。

6 个答案:

答案 0 :(得分:1)

您的问题正在发生,因为下拉选项会更改searchTerm表单控件的值;从而在该表单控件上发出valueChanges可观察事件的事件。

有几种方法可以在窗体控件上的valueChanges observable中构建一些逻辑,以便忽略这个不需要的Http GET请求。

天真的解决方案

天真的解决方案是存储下拉选项选择值,并跳过selectionValue === searchTerm.value的可观察逻辑。

var optionSelection; // set this value on selection of a dropdown option
this.searchTerm.valueChanges
  .debounceTime(400) 
  .subscribe(data => {
    if (optionSelection !== searchTerm.value) {
      this.service.search_word(data).subscribe(response =>{
        this.searchResult = response
      })
    }
  })

更好的解决方案

FormControl类有一个setValue函数,它有一个可选参数来指定是否向valueChanges observable发出事件。

setValue(value: any, options: {
    onlySelf?: boolean;
    emitEvent?: boolean;
    emitModelToViewChange?: boolean;
    emitViewToModelChange?: boolean;
} = {}): void

通过更改下拉选项选择以使用setValue设置searchTerm表单控件值,您可以传递可选的emitEvent: false参数并停止发出此额外的可观察事件。

searchTerm.setValue(dropdownValue, { emitEvent: false });
  

如果emitEvent为false,则此更改将导致不发出FormControl上的valueChanges事件。默认为true(因为它落到updateValueAndValidity)。

答案 1 :(得分:1)

问题在于有两个事件被触发的事件...我已经为它制造了一个错误,但它应该如何工作。希望这会有所帮助。

这是我的解决方案:

HTML:

<mat-option *ngFor="let item of searchResult" [value]="item.Id" (onSelectionChange)="onSelectionChange($event, item.Id)"> // or item

在您的组件中:

private onSelectionChange(_event: any, _id: any) {
  if (_event.isUserInput === true) { // there are two events one with true and one with false;
    this.service.search_word(_id).subscribe(response =>{
         this.searchResult = response
    })
}

答案 2 :(得分:1)

您可以在输入文字中使用Jobs/Honda,然后就可以像这样使用

(keyup)="onKeyUp($event)"

答案 3 :(得分:0)

从Angular Material版本7.2.0开始,对于MatAutocomplete,您有一个名为“ optionSelected”的新EventEmitter

所以您的HTML应该是这样的:

<mat-autocomplete (optionSelected)="optionSelected($event)" #auto1="matAutocomplete" [displayWith]="displayFn">
    <mat-option *ngFor="let item of searchResult" [value]="item.Id">
      {{ item.Name }}
    </mat-option>
  </mat-autocomplete>

和您的.ts:

private optionSelected(event) {
  console.log(event)
}

您将在optionSelected()方法内处理它。

尝试一下,因为我没有您的所有代码。让我知道它是否无效。

答案 4 :(得分:0)

这是一种棘手的方法,将选项值设置为 object 而不是 string ,然后在输入更改时可以确定是通过键入还是选择来确定。

<!-- Template -->
<mat-option *ngFor="let item of searchResult" [value]="item">

// Component
this.searchTerm.valueChanges.subscribe(modelValue => {
  if (typeof modelValue === 'string') {
    this.searchUser.next(modelValue.toString());
  } else {
    this.searchTerm.setValue(modelValue.Id, {
      emitEvent: false
    });
  }
});

答案 5 :(得分:0)

您可以使用keyup代替valueChanges。以下是我解决相同问题的方法。请记住,此示例在此处显示了我使用mat-autocomplete进行键盘锁的用例。

import { ViewChild, ElementRef } from '@angular/core';
import { fromEvent } from 'rxjs';
import { debounceTime, tap, switchMap, map, filter, distinctUntilChanged } from 'rxjs/operators';

添加mat-autocomplete html,请注意,这还包括在远程进行HTTP调用期间加载微调器,但可以将其删除。

 <mat-form-field>
   <input matInput #searchInput placeholder="" [matAutocomplete]="auto">             
   <mat-autocomplete #auto="matAutocomplete">
    <mat-option *ngIf="isLoading">
     <mat-spinner diameter="50"></mat-spinner>
    </mat-option>
    <ng-container *ngIf="!isLoading">
     <mat-option *ngFor="let data of filteredData | async" [value]="data.modelValue">
      <span>{{ data.modelValue }}</span>
     </mat-option>
    </ng-container>
   </mat-autocomplete>
  </mat-form-field>

将以下内容添加到您的控制器中。

isLoading: boolean;
@ViewChild('searchInput') searchInput: ElementRef;
filteredData: searchModel[] = [];

设置keyup事件监听器。

fromEvent(this.searchInput.nativeElement, 'keyup').pipe(
  map((event: any) => {
    return event.target.value; // Map search input value
  }),
  filter(res => res.length > 3), // If character length greater then 3
  debounceTime(500),
  distinctUntilChanged(), // If previous search is different from current
  tap((val) => {
    this.isLoading = true;
    this.filteredData = [];
  }),
  switchMap(value =>
    this.service.search(value).pipe(
      finalize(() => this.isLoading = false)
    )

  )
).subscribe(data => {
  this.filteredData = data;
});