Обзор

Прежде всего, давайте посмотрим, что я имею в виду под «Динамическими ярлыками». Представьте себе проект, в котором есть множество пользователей, и каждому из них нужен ярлык или горячая клавиша для открытия информационного модального окна. Конечно, не совсем уместно заставлять всех пользователей использовать только определенный ярлык для этого действия. Даже у некоторых пользователей может быть глобальный ярлык, точно такой же, как у вас, который имеет совершенно другую функциональность в другом программном обеспечении.

И вот тут-то и появляются «Динамические ярлыки»! Использование динамических сочетаний клавиш позволяет использовать разные горячие клавиши для любых действий в зависимости от того, что удобно пользователям. Поэтому нам необходимо получить всю пользовательскую конфигурацию пользователя при входе в систему.

Чтобы добиться динамических ярлыков в angular, нам нужно реализовать две основные части:

  • Структура данных
  • Ключевой слушатель

Служба и структура данных

Начните с создания службы для хранения всей логики и обработчиков с помощью angular cli:

Предполагая, что вы следуете руководству по стилю angular для структуры вашего проекта.

ng generate service core/services/shortcut

Затем, чтобы имитировать конфигурации ярлыков пользователя, мы создаем интерфейс для представления структуры данных ярлыков. Наш интерфейс может быть примерно таким:

export interface Shortcut {
    /**
     * Our shortcut is defined here as a string 
     */
    key: string;
    /**
     * An identifier about what this shortcut is going to do
     */
    action: string;
}

И мы помещаем этот интерфейс в файл с именем shortcut.service.d.ts рядом с нашей службой ярлыков. Теперь у нас есть ярлыки для примера в поле класса ShortcutService:

configuration: Shortcut[] = [
    {
      key: 'Ctrl+B',
      action: 'GoToHome'
    },
    {
      key: 'Ctrl+M',
      action: 'Logout',
    }
];

Итак, как вы видите, здесь я хочу перейти на домашнюю страницу, когда нажимаю Ctrl+B. И я хочу выйти, используя Ctrl+M.

Реализовать прослушиватель ключей

Теперь, когда у нас есть ярлыки, мы должны быть в состоянии прослушивать событие клавиатуры keydown на цели window. Для этого лучше всего создать наблюдаемое из события с помощью rxjs и сохранить его в поле в нашем сервисе:

В соответствии с соглашением об именовании наблюдаемых объектов мы назовем его keydown$.

keydown$ = fromEvent(window, 'keydown');

Нам также нужно поле для хранения подписки, чтобы мы могли отписаться от нее и избежать утечки памяти:

keydownSubscription: Subscription;

Затем мы добавляем метод для подписки на этот пользовательский наблюдаемый объект, чтобы мы могли вызывать его из других компонентов, чтобы начать прослушивание:

start(): void {
    this.keydownSubscription = this.keydown$.subscribe((event: KeyboardEvent) => {
        for (const shortcut of this.configuration) {
            let shouldBeCtrl = false;
            let shouldBeAlt = false;
            let targetKey = '';

            // Parse shortcut text to know what should be keys
            const keys = shortcut.key.toLocaleLowerCase().split('+');
            for (const key of keys) {
                switch (key) {
                case 'ctrl': shouldBeCtrl = true; break;
                case 'alt': shouldBeAlt = true; break;
                default: targetKey = key;
                }
            }

            if (
                event.ctrlKey === shouldBeCtrl
                && event.altKey === shouldBeAlt
                && targetKey === event.key
            ) {
                this.actionHandler(shortcut.action);
                break;
            }
        }
    });
}

Как вы видите, в этом методе мы анализируем каждое сочетание клавиш, чтобы увидеть, каким должно быть событие, соответствующее нашему ярлыку. Затем, если все является тем, чем оно «должно быть», мы можем вызвать обработчик действий и разорвать цикл. Обработчик действия может быть таким:

actionHandler(action: string): void {
    switch (action) {
        case 'GoToHome': /* Ex: Navigate to home */ break;
        case 'Logout': /* Ex: Clear the local storage */ break;
        default: /* Warning about invalid action */
    }
}

И, наконец, последний метод — это не что иное, как метод stop для отмены подписки на этот пользовательский наблюдаемый объект и отмены прослушивания событий клавиатуры:

stop(): void {
    if (this.keydownSubscription) {
        this.keydownSubscription.unsubscribe();
    }
}

Теперь угадайте, что?

У вас могут быть разные ярлыки для разных страниц в вашем приложении, и это совершенно нормально! Просто добавьте этот метод:

changeShortcuts(shortcuts: Shortcut[]) {
    this.configuration = shortcuts;
}

И теперь вы можете изменить набор ярлыков и действий, когда захотите :)