Поиск по этому блогу

пятница, 14 февраля 2014 г.

Навигация по p:datatable при помощи клавиатуры


Сегодня, передо мной была поставлена задача навигации по строкам таблицы c помощью клавиатуры в primefaces. После изучения сатйов primefaces и primefaces extensions выяснилось, что данная возможность не реализована.
На форуме нам предлагают самим реализовать эту функциональность, так как:
This feature doesn't have high priority at the moment unless someone sponsors it.
что переводится как:
На данный момент, данная функциональность имеет низкий приоритет и не будет
реализована пока ее кто-нибудь не оплатит.
(от автора: видимо очень низкий, дата поста 21.06.2012)
В тикете на багтрекере, есть вариант реализации данной задачи, но он плох тем, что генерирует лишние запросы на сервер и лишний трафик. Так как больше вариантов найдено не было, я решил написать собственный (ну и само собой поделиться им с вами). Для реализации это задачи нам понадобиться primefaces версии 4+. Я не буду описывать создание всего приложения, а опишу только как добавить возможность навигации для datatable. Просмотреть и загрузить весь проект вы можете на GitHub.

Предположим у нас есть следующая таблица:
<p:dataTable id="dataTable" widgetVar="table" ...>
    ...
</p:dataTable>  
Для обработки нажатий клавиш на клавиатуре, мы могли бы использовать компонент primefaces <p:hotkey ...>. Но в этом случае клавиши переназначались бы для всей страницы, а нам необходимо их оставить для других компонентов или для скролла в браузере, поэтому будем использовать jquery. Для того чтобы повесить обработчик на таблицу, нам необходимо добавить к ней аттрибут tabindex, но разработчики primefaces лишили нас этой возможности, поэтому я решил особо не заморачиваться и обернул таблицу в div с аттрибутом tabindex = 1, вот так:
<div tabindex="1" id="table_div">
    <p:dataTable id="dataTable" widgetVar="table" ...>
        ...
    </p:dataTable>  
</div>
Теперь мы можем вешать обработчик событий с клавиатуры на нашу таблицу. Кроме того, благодаря tabindex, эти события будут перехватываться только тогда, когда таблица находиться в фокусе. Напишем обработчик клавиши вниз:
$("#table_div").keydown(function(event) {
    if (event.which === 40) {
        event.preventDefault();
        alert("You press down key.")
    }
});
Теперь напишем функцию, которая будет выделять следующую строку в таблице и заменем бессмысленный alert на вызов этой функции:
var down = function() {
    //Получаем индекс выбранной строки, если строка не выбрана вернет 0
    var index = PF('table').originRowIndex;
    //увеличиваем индекс на 1
    index++;
    //убираем выделение со всех строк
    PF('table').unselectAllRows();
    //выделяем новую строку
    PF('table').selectRow(index);
};
Как видно из кода мы используем методы объекта, возвращаемого PF(‘table’), где “table” - это имя виджета widgetVar.
Если мы сейчас запустим наше приложение и попробуем нажать клавишу вниз, то ничего не произойдет, так как табица не в фокусе, и это хорошо. Теперь выделим любую строку в нашей таблице и нажмем клавишу вниз - выделение перешло на следующую строчку - супер!
В эйфории мы продолжаем щелкать клавишу вниз, но тут обнаруживается, что выделенная строка не меняется. Что же произошло? А то, что метод selectRow(int index), почему-то не обновляет originRowIndex. Ну что же, придется сделать этого за него. Добавляем в конец нашего кода следующую строчку:
...
PF('table').selectRow(index);
//change originRowIndex value
PF('table').originRowIndex = index;
Пробуем… Теперь все работает, но… Когда мы доходим до конца нашей таблицы, выделение пропадает, а точнее уходит за ее пределы. Давайте же исправим это. Для этого воспользуемся количеством дочерних элементов в таблице и наш код теперь выглядит так:
var down = function() {
    //get index of selected row, if no row is selected return 0
    var index = PF('table').originRowIndex;
    //get amount of rows in the table
    var rows = PF('table').tbody[0].childElementCount;
    //increase row index
    index++;
    //if new index equals number of rows, set index to first row
    if (index === rows) {
        index = 0;
    }
    //deselect all selected rows
    PF('table').unselectAllRows();
    //select new row 
    PF('table').selectRow(index);
    //change originRowIndex value
    PF('table').originRowIndex = index;
};

$("#table_div").keydown(function(event) {

    if (event.which === 40) {
        event.preventDefault();
        down();
    }

});
Сделаем то же самое для клавиши вверх:
var up = function() {
    var rows = PF('table').tbody[0].childElementCount;
    var index = PF('table').originRowIndex;

    if (index === 0) {
        index = rows - 1;
    } else {
        index--;
    }

    PF('table').unselectAllRows();
    PF('table').selectRow(index);
    PF('table').originRowIndex = index;
}

$("#table_div").keydown(function(event) {

    if (event.which === 38) {
        event.preventDefault();
        up();
    }

    if (event.which === 40) {
        event.preventDefault();
        down();
    }

});
Как можно убедить все работает, но есть еще один баг. Если выделить таблицу, щелкнув по ее заголовку, и нажать клавишу вниз, то мы увидим что выделиться вторая строка. Можно было бы конечно оставить так, но не нужно. Испрвить это очень лекго. В виджете таблицы есть поле selection, которое хранит массив выделенных элементов, им мы и воспользуемся. Добавим в начало метода down следующие условие:
//If no rows selected, select first row
if(PF('table').selection.length === 0){
    PF('table').selectRow(0);
    return;
}
Вот и все, наша талбица готова. Я также добавил туда возможность выбора элемента нажатием клавиши enter, но не буду описывать это в этой статье. Если есть желание, можете скачать код с GitHub. Также здесь реализована возможность только одиночного выбора, если понадобиться, могу написать статью по множественному.

Комментариев нет:

Отправить комментарий