Using keyboard to select items in md-select

2020-07-29 17:41发布

问题:

I'm using augular/material2 md-select. When I tab into the md-select component I can use the up and down arrows to select items. I can also press the space bar to see the list and use the up and down arrows and the enter key to select an item. What I can't do is press the first letter of an item and have that item selected.

Using the example on https://material.angular.io/components/component/select I'd expect to be able to tab into the md-select component and press P to select Pizza. Is this not supported? Is there a work around?

Thanks

回答1:

In material doc, only following keyboard interactions are supported:

  • DOWN_ARROW: Focus next option
  • UP_ARROW: Focus previous option
  • ENTER or SPACE: Select focused item

The functionality of 'press the first letter of an item and have that item selected' is difficult to implement, because its hard to predict, how many items with same initial letter might exist in the list. If the list has items PIZZA, POPCORN, PASTA, which item should be selected by pressing 'P'?

If you are thinking of filtering items by letters pressed, then you might want to have a look at md-autocomplte.

https://material.angular.io/components/component/autocomplete



回答2:

Here is the class that I wrote to handle this case since it no longer works as the default implementation.

class FindValue {
    lastIndex: number = -1;

    constructor(private _toString?: Function) {
    }

    findIndex(values, startIndex, char) {
        for (let ii = startIndex; ii < values.length; ii++) {
            let value;
            if (this._toString) {
                value = this._toString(values[ii]);
            } else {
                value = values[ii];
            }
            if (value && value.toLowerCase().startsWith(char)) {
                return ii;
            }
        }
        return -1;
    }

    find(values, char) {
        let value;
        let index = this.findIndex(values, this.lastIndex + 1, char);
        if (index < 0 && this.lastIndex >= 0) {
            index = this.findIndex(values, 0, char);
        }

        if (index >= 0) {
            this.lastIndex = index;
            return values[index];
        }

        return null;
    }
}

If I add (keypress)="selectKeyPress($event)" to the mat-select component, I then implement that like this.

constructor() {
    this.findValue = new FindValue(value => value.name);
}

selectKeyPress($event) {
    var inp = String.fromCharCode($event.keyCode);
    if (/[a-zA-Z]/.test(inp)) {
        let value = this.findValue.find(this.values, inp);
        if (value) {
            // Set the ngModel value to this value.
        }
    }
}

The object will keep state of last found index such that it will cycle through all the items that start with the letter pressed, if you continuously press the same letter, and come around again to the first if it is the last one. My object had a name attribute that I use for displaying in the select box. If you just have a string object you can leave off the function in the constructor call.