【问题标题】:How to make tab key as enter key in Angular Material?如何在 Angular Material 中将制表键作为输入键?
【发布时间】:2019-08-04 23:52:47
【问题描述】:

这是我的角度材料自动完成代码

<input type="search" id="setId" name="setId" [attr.list]='collectionType' [(ngModel)]="selValue" class="text-box"
  placeholder="--Select--" (focus)="ValidateParent()" (keyup.tab)="test()" (keyup)="EmitValues($event)" [id]="setId"
  [matAutocomplete]="auto" [title]="selValue" [placeholder]='WaterMarkText'>


<div [hidden]="IsCascading">
  <mat-autocomplete [id]="collectionType" #auto="matAutocomplete" (optionSelected)='onChange($event)'>
    <mat-option *ngFor="let items of codeList" [value]="items.text" [attr.data-text]='items.text' [id]="items.value">
      {{items.text}}
    </mat-option>
  </mat-autocomplete>
</div>

Angular 材质在tab 选择方面存在问题。比如单击tab 按钮时,材质自动完成无法选择值。但它在单击enter 按钮时工作。因此,我需要手动覆盖tab 键事件上的enter 键事件。怎么可能?

【问题讨论】:

标签: html angular events autocomplete angular-material


【解决方案1】:

改进我的评论,根据回复我们可以创建一个指令

import {
    Directive,
    AfterViewInit,
    OnDestroy,
    Optional
} from '@angular/core';
import {
    MatAutocompleteTrigger
} from '@angular/material';


@Directive({
    selector: '[tab-directive]'
})
export class TabDirective implements AfterViewInit, OnDestroy {
    observable: any;
    constructor(@Optional() private autoTrigger: MatAutocompleteTrigger) {}
    ngAfterViewInit() {
        this.observable = this.autoTrigger.panelClosingActions.subscribe(x => {
            if (this.autoTrigger.activeOption) {
                this.autoTrigger.writeValue(this.autoTrigger.activeOption.value)
            }
        })
    }
    ngOnDestroy() {
        this.observable.unsubscribe();
    }
}

你使用:

<input tab-directive type="text" matInput [formControl]="myControl" 
      [matAutocomplete]="auto" >

(见stackblitz

更新我们只能控制tab.key,否则你总是关闭,你得到选择的值,所以

@Directive({
    selector: '[tab-directive]'
})
export class TabDirective {
    observable: any;
    constructor(@Optional() private autoTrigger: MatAutocompleteTrigger) {}

    @HostListener('keydown.tab', ['$event.target']) onBlur() {
        if (this.autoTrigger.activeOption) {
            this.autoTrigger.writeValue(this.autoTrigger.activeOption.value)
        }
    }

}

(see a new stackblitz)

更新 2 我不相信这个答案有这么多赞成,因为它是错误的。作为@Andrew allen cmets,该指令不会更新控件。好吧,已经晚了,但我试图解决。一种选择是使用

this.autoTrigger._onChange(this.autoTrigger.activeOption.value)

另一个想法是注入 ngControl,所以

constructor(@Optional() private autoTrigger: MatAutocompleteTrigger,
    @Optional() private control: NgControl) {}
ngAfterViewInit() {
    this.observable = this.autoTrigger.panelClosingActions.subscribe(x => {
        if (this.autoTrigger.activeOption) {
            const value = this.autoTrigger.activeOption.value;
            if (this.control)
                this.control.control.setValue(value, {
                    emit: false
                });
            this.autoTrigger.writeValue(value);
        }
    })
}

【讨论】:

  • 不是所有的英雄都穿斗篷......我在github.com/angular/components/issues/4951的讨论中引用了这个答案@你太棒了我的朋友
  • @AndrewAllen,感谢您刷新我,但我想,我更新了我的答案,只得到值是你按下 tab 键(否则,转义键也选择值)
  • 有什么方法可以使输入重新验证或更改输入的无效值?目前这显示了我的垫子错误“必需”
  • 如果您将 stackblitz 更改为 myControl = new FormControl('', Validators.required);执行 this.autoTrigger._onChange(this.autoTrigger.activeOption.value);writeValue 似乎可以解决验证问题
  • @AndrewAllen,您对使用this.autoTigger._OnChange 的想法很棒。我做了另一种方法 - 注入 ngControl- 。有一个小问题是我们希望 obtions 是一个对象的属性,例如[value]="option.name" 因为writeValue 需要整个对象,并且在这种情况下this.autoTrigger.activeOption 只给我们“名称”
【解决方案2】:

我参加聚会有点晚了;所以 Eliseo 的回答有一个烦人的问题:即使用户已经通过鼠标单击从面板中选择了不同的选项,它也会自动完成。我最终想出的解决方法一点也不直截了当。

    /**
     * Selects currently active item on TAB
     * @example
     * <input vxTabActiveOption [matAutocomplete]="matAutocomplate">
     * <mat-autocomplete #matAutocomplate="matAutocomplete" autoActiveFirstOption>
     */
    @Directive({ selector: '[vxTabActiveOption]' })
    export class TabActiveOptionDirective implements AfterViewInit, OnDestroy {
      private readonly destroy$ = new Subject<void>();
    
      /**
       * Whether or not the autocomplete panel was open before the event
       */
      private panelOpen = false;
    
      constructor(
        private autoTrigger: MatAutocompleteTrigger,
        private control: NgControl
      ) { }
    
      ngAfterViewInit() {
        const autocomplete = this.autoTrigger.autocomplete;
        merge(
          autocomplete.opened.pipe(map(() => true)),
          autocomplete.closed.pipe(map(() => false))
        ).pipe(
          takeUntil(this.destroy$),
          delay(0)
        ).subscribe(value => this.panelOpen = value);
      }

      @HostListener('keydown.tab')
      onBlur() {
        if (this.panelOpen && this.autoTrigger.activeOption) {
          const value = this.autoTrigger.activeOption.value;
          this.control.control.setValue(value, { emit: false });
          this.autoTrigger.writeValue(value);
        }
      }
    
      ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
      }
    }

【讨论】:

    【解决方案3】:

    是的,我知道这是一个老问题,但有一个更好的方法是使用类似的指令

    @Directive({ selector: '[tab-selected]' })
    export class TabSelected implements AfterViewInit {
      constructor(private auto: MatAutocomplete) {}
      ngAfterViewInit() {
        this.auto._keyManager.onKeydown = (event: KeyboardEvent) => {
          switch (event.keyCode) {
            case TAB:
              if (this.auto.isOpen) {
                const option = this.auto.options.find(x => x.active);
                if (option) {
                  option.select();
                  event.preventDefault();
                  return;
                }
              }
              this.auto._keyManager.tabOut.next();
              break;
            case DOWN_ARROW:
              this.auto._keyManager.setNextItemActive();
              break;
    
            case UP_ARROW:
              this.auto._keyManager.setPreviousItemActive();
              break;
          }
        };
      }
    }
    

    你用喜欢

    <mat-form-field class="example-full-width" appearance="fill">
      <mat-label>Number</mat-label>
      <input type="text"
             placeholder="Pick one"
             aria-label="Number"
             matInput
             [formControl]="myControl"
             [matAutocomplete]="auto">
      <mat-autocomplete tab-selected #auto="matAutocomplete"
      autoActiveFirstOption >
        <mat-option  *ngFor="let option of filteredOptions | async" [value]="option">
          {{option}}
        </mat-option>
      </mat-autocomplete>
    </mat-form-field>
    

    您可以在this stackblitz工作

    【讨论】:

    • 谢谢 - 这很好用,但我发现如果我随后返回到该字段并且 Angular 重新打开面板,您将被困在该字段中。将 "if (option)" 更改为 "if (option && !option.selected)" 以解决此问题。我的偏好也是 tab 键仍然前进到下一个字段,所以我删除了 preventDefault() 以便允许这种情况发生。
    猜你喜欢
    • 1970-01-01
    • 2020-11-24
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多