Каков предпочтительный способ сохранения состояния между init и update для пользовательской привязки нокаута?
В настоящее время я сохраняю состояние, используя данные jQuery для элемента dom.
ko.bindingHandlers.customValue = {
init: function init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var state = { isEditing: false };
$(element).focus(function focus() {
state.isEditing = true;
}).blur(function blur() {
state.isEditing = false;
}).data("customBinding", state);
},
update: function update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// ignore if updating
if (!$(element).data("customBinding").isEditing) {
// handle update if they are not updating
}
}
};
Есть ли лучшее место для хранения состояния для привязки, которое не требует dom? Может ли bindingContext использоваться для хранения состояния для каждого экземпляра привязки?
Ответы
Ответ 1
bindingContext
- это возможность, но только для передачи данных от init
до update
при первом запуске привязки. В следующий раз, когда срабатывает update
, его больше не будет.
На самом деле существует два варианта сохранения состояния этого типа:
1- На элементе, как вы заявили. Вы можете использовать jQuery $.data
, или KO включает API для этого, а также ko.utils.domData.get(element, key)
и ko.utils.domData.set(element, key, value)
.
2- Поместите этот тип информации в свою модель просмотра, если это необходимо. Флаг для обозначения isEditing
не обязательно неуместен в модели представления. Я лично хотел бы поместить этот тип "метаданных" в качестве суб-наблюдаемых с наблюдаемого типа:
var name = ko.observable("Bob");
name.isEditing = ko.observable(false);
Вы могли бы привязываться к name
и name.isEditing
.
Это имеет некоторые преимущества:
- сохраняет модель обзора довольно чистой, так как вы не вводите новые свойства верхнего уровня.
- сохраняет суб-наблюдаемое привязанное к родительскому наблюдаемому (нет необходимости в
nameIsEditing
и т.д.)
- когда он превращается в JSON с чем-то вроде
ko.toJSON
, суб-наблюдаемый isEditing
будет просто удален при распаковке родителя. Таким образом, вы не будете отправлять ненужные значения обратно на сервер.
- в этом случае он также может иметь преимущество в том, что он доступен для других вычислений в вашей модели просмотра или для привязки к нескольким элементам вашего пользовательского интерфейса.
Ответ 2
Прикрепление данных к элементу прекрасно, и Knockout использует этот метод для привязки потока управления (если, с и т.д.), например.
Другим методом является использование функции init
и использование вычисленного наблюдаемого для обработки обновлений. Я использую этот метод в привязке repeat. Вот важные части:
ko.bindingHandlers['repeat'] = {
'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
...
// set up persistent data
var lastRepeatCount = 0;
...
ko.computed(function() {
var repeatCount = ko.utils.unwrapObservable(valueAccessor());
...
// Remove nodes from end if array is shorter
for (; lastRepeatCount > repeatCount; lastRepeatCount--) {
...
}
...
// Add nodes to end if array is longer (also initially populates nodes)
for (; lastRepeatCount < repeatCount; lastRepeatCount++) {
...
}
}, null, {'disposeWhenNodeIsRemoved': placeholder});
...
}
};
Ответ 3
Я часто использую этот шаблон:
define(['knockout'], function(ko) {
var interInstanceVariable = null;
function Tree(element) {
var privateInstanceVariable = null;
function privateInstanceMethod() {}
this.publicInstanceMethod = function() {}
}
ko.bindingHandlers.cannDendrogram = {
init: function(element, valueAccessor) {
$(element).data('tree', new Tree(element));
},
update: function(element, valueAccessor) {
var tree = $(element).data('tree');
tree.publicMethod();
}
};
});
Ответ 4
Я понимаю, что этот вопрос старый, но я наткнулся на него, ища подход, поэтому подумал, что я придумаю более современный метод ko.
Вы можете просто добавить свойства к bindingContext. $data напрямую. Я выбрал раздражающее имя переменной "___IsEditing", чтобы избежать потенциальных столкновений, но вы получаете идею...
ko.bindingHandlers.customValue = {
init: function init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
bindingContext.$data.___IsEditing = false;
$(element).focus(function focus() {
bindingContext.$data.___IsEditing = true;
}).blur(function blur() {
bindingContext.$data.___IsEditing = false;
}).data("customBinding", state);
},
update: function update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// ignore if updating
if (bindingContext.$data.___IsEditing) {
// handle update if they are not updating
}
}
};
Ответ 5
Я использую функцию для создания общей области для init и update, определяя общие данные внутри функции.