Knockout.js предоставляет мощный механизм привязки данных между
моделью представления (ViewModel) и пользовательским интерфейсом. Помимо
стандартных встроенных привязок, таких как text,
value или visible, фреймворк позволяет
создавать пользовательские привязки, которые расширяют
функциональность и упрощают повторное использование кода.
Пользовательская привязка представляет собой объект с определёнными
методами, который регистрируется через
ko.bindingHandlers.
ko.bindingHandlers.highlight = {
init: function(element, valueAccessor) {
const value = ko.unwrap(valueAccessor());
element.style.backgroundColor = value ? 'yellow' : 'transparent';
},
update: function(element, valueAccessor) {
const value = ko.unwrap(valueAccessor());
element.style.backgroundColor = value ? 'yellow' : 'transparent';
}
};
В данном примере создаётся привязка highlight, которая
окрашивает фон элемента в жёлтый, если значение истинно.
init — вызывается один раз при
инициализации элемента. Здесь выполняется настройка, установка
обработчиков событий или начальная модификация DOM.
update — вызывается каждый раз при
изменении привязанного значения. Этот метод гарантирует реактивное
обновление интерфейса в соответствии с текущими данными модели.
Привязки могут использовать только update, если
начальная настройка не требуется:
ko.bindingHandlers.uppercase = {
update: function(element, valueAccessor) {
const value = ko.unwrap(valueAccessor());
element.textContent = value.toUpperCase();
}
};
Метод valueAccessor возвращает функцию, которая
возвращает текущее привязанное значение. Для доступа к его реальному
значению используется ko.unwrap, позволяющий работать как с
обычными значениями, так и с observables.
const value = ko.unwrap(valueAccessor());
Если привязка должна обрабатывать несколько параметров, можно передавать объект:
<div data-bind="fadeVisible: { show: isVisible, duration: 500 }"></div>
ko.bindingHandlers.fadeVisible = {
update: function(element, valueAccessor) {
const options = ko.unwrap(valueAccessor());
if (options.show) {
$(element).fadeIn(options.duration);
} else {
$(element).fadeOut(options.duration);
}
}
};
Внутри пользовательских привязок доступен контекст
данных через allBindings,
bindingContext и element.
allBindings — объект всех привязок текущего
элемента.bindingContext — полный контекст, включая родительские
модели.element — DOM-элемент, к которому применяется
привязка.Пример использования контекста:
ko.bindingHandlers.showParentName = {
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
element.textContent = bindingContext.$parent.name();
}
};
Привязки могут управлять событиями DOM через стандартные обработчики:
ko.bindingHandlers.clickLogger = {
init: function(element) {
element.addEventListener('click', function() {
console.log('Элемент был нажат');
});
}
};
Если обработчик зависит от данных модели, используется
valueAccessor:
ko.bindingHandlers.clickAction = {
init: function(element, valueAccessor) {
element.addEventListener('click', function() {
valueAccessor()(); // вызываем функцию из модели
});
}
};
Knockout автоматически отслеживает observables внутри
update. Если привязанное значение изменится, метод
update будет вызван повторно:
ko.bindingHandlers.showDate = {
update: function(element, valueAccessor) {
const date = ko.unwrap(valueAccessor());
element.textContent = date.toLocaleDateString();
}
};
const viewModel = {
currentDate: ko.observable(new Date())
};
Каждое обновление currentDate автоматически обновит
отображение.
Можно создавать привязки, которые добавляют функциональность к
стандартным. Например, модифицируем text для форматирования
чисел:
ko.bindingHandlers.formattedText = {
update: function(element, valueAccessor) {
const value = ko.unwrap(valueAccessor());
element.textContent = value.toLocaleString();
}
};
Использование:
<span data-bind="formattedText: price"></span>
init используется для одноразовой настройки элемента и
подписки на события.update отвечает за реактивное изменение DOM в ответ на
изменения модели.ko.unwrap.bindingContext.$parent и $root.Создание собственных привязок позволяет строить модульные, легко переиспользуемые компоненты, полностью интегрированные с системой реактивности Knockout.js.