понедельник, 23 января 2012 г.

Цепляем jQuery Datepicker c помощью кастомного биндинга на KnockoutJS

Как я говорил в предыдущей статье сегодня мы создадим свою "команду" ну или правильному custom binding (кастомный биндинг).
Что это такое ?
В knockout для того, чтобы с вашим элементом происходили какие-то махинации: скрыть, сделать неактивным, добавить стиль, присвоить значение и тп существуют команды, некоторые из которых вы уже видели в примерах - value, text, visible, ckecked, enable, options. Эти команды предопределены в фреймворке и предоставляют базовый и достаточный функционал для реализации ваших нужд.
Иногда бывают случаи когда код выглядит когда их вроде бы не хватает. Например, обавим в наши фильтры поддержку типа Date и когда пользователь выбрал поле, например Issue Date, установил курсор чтоб ввести значение, я хочу чтоб он не вводил его руками, а выбрал дату через jQuery datepicker. Естественно я хочу чтоб выбор даты срабатывал только для Date типа фильров, а для других Number и String оставался просто полем ввода.
Без своей кастомной команды, нам бы пришлось подписаться через subscribe на ColumnObj и смотреть при выборе поля из выпадающего списка его тип. Если это дата - применить datepicker, если нет - удалить datepicker. Если мы в будущем захотим добавить еще какую-то логику, то пойдут кастыли и будет все в куче. 
Тут нам как раз и поможет кастомный биндинг (команда). В итоге наш шаблон будет выглядеть так:
 
    <input data-bind="value: Value, datePicker: Type" type="text" />

Вся логика для выбора даты будет вынесена в команду datePicker. Собственно код:

ko.bindingHandlers.datePicker = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var type = ko.utils.unwrapObservable(valueAccessor());

        if (type == 'Date') {
            $(element).datepicker();
        }

    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var type = ko.utils.unwrapObservable(valueAccessor());

        if (type == 'Date') {
            $(element).datepicker();
        }
        else {
            $(element).datepicker("destroy");
        }
    }
}


Я не буду описывать все опции в деталях, о них выможете прочитать здесь.
Рассмотром, что и как.  Как мы видим для того чтобы создать команду, мы создаем объект с соответствующим называнием команды в ko.bindingHandlers. У нашего объекта должно быть две функции.

init - вызывается, когда knockout доходит до места в html, где установленна наша команда.
update - вызывается, когда изменится значение поля за которым мы следим с помощью нащей команды.
В нашем случае, data-bind="dataPicker: Type, value: Value" значит - следить за полем Type и если оно изменится вызвать команду datePicker. Она вызовится при инициализации и после последющих обновлений.
Что происходит в обработчиках?

valueAccessor() - возвращает поле, к которому привязана команд. Это всегда будет функция. т.к. все поля объявляются через ko.obsorvable или ko.computed и они возврашают функции.
ko.utils.unwrapObservable - это функция, которая вычисляет значение поля на такущий момент, когда вызывается эта строка.
Дальше, я думаю, все понятно. Параметры, которые передаютися в init и update очевидны, кроме allBindingsAccessor, но и пользоваться им вы врядли будете.

Ниже рабочий пример. Я добавил тип Date в Conditions, саму команду datePicker и поле Issue Date.

1 комментарий:

  1. Ага, сначала было не понятно, зачем использовать фреймверк для инициализации даты. В динамике дело.

    ОтветитьУдалить