Уникальные идентификаторы в шаблонах knockout.js
Предположим, у меня есть шаблон knockout.js следующим образом:
<script type="text/html" id="mytemplate">
<label for="inputId">Label for input</label>
<input type="text" id="inputId" data-bind="value: inputValue"/>
</script>
Если я создаю этот шаблон в нескольких местах на странице, я получаю несколько входов с одним и тем же идентификатором (и несколькими ярлыками с тем же значением), что имеет плохие последствия. В частности, весь код, который зависит от идентификаторов, может работать некорректно (в моем случае я использую плагин jquery.infieldlabel, который путается несколькими входами с одним и тем же идентификатором). Как я решаю эту проблему сейчас, я добавляю уникальный атрибут id к модели, которую я привязываю к шаблону:
<script type="text/html" id="mytemplate">
<label data-bind="attr: {for: id}>Label for input</label>
<input type="text" data-bind="attr: {id: id}, value: inputValue"/>
</script>
Это работает, но это не очень элегантно, так как я должен иметь этот искусственный атрибут id в своих моделях, который не используется ни для чего другого. Интересно, есть ли здесь лучшее решение.
Ответы
Ответ 1
Альтернативой, которая не полагается на порядок привязки полей, является наличие привязки, устанавливающего свойство id
для самих данных, что должно быть наблюдаемым.
ko.bindingHandlers.uniqueId = {
init: function(element, valueAccessor) {
var value = valueAccessor();
value.id = value.id || ko.bindingHandlers.uniqueId.prefix + (++ko.bindingHandlers.uniqueId.counter);
element.id = value.id;
},
counter: 0,
prefix: "unique"
};
ko.bindingHandlers.uniqueFor = {
init: function(element, valueAccessor) {
var value = valueAccessor();
value.id = value.id || ko.bindingHandlers.uniqueId.prefix + (++ko.bindingHandlers.uniqueId.counter);
element.setAttribute("for", value.id);
}
};
Вы бы использовали его как:
<ul data-bind="foreach: items">
<li>
<label data-bind="uniqueFor: name">Before</label>
<input data-bind="uniqueId: name, value: name" />
<label data-bind="uniqueFor: name">After</label>
</li>
</ul>
Пример: http://jsfiddle.net/rniemeyer/JjBhY/
Хорошая вещь о добавлении свойства к наблюдаемой функции заключается в том, что, когда вы превращаете его в JSON для отправки обратно на сервер, он просто естественным образом исчезает, поскольку наблюдаемый просто превращается в его развернутое значение.
Ответ 2
В прошлом я сделал что-то подобное:
ko.bindingHandlers.uniqueId = {
init: function(element) {
element.id = ko.bindingHandlers.uniqueId.prefix + (++ko.bindingHandlers.uniqueId.counter);
},
counter: 0,
prefix: "unique"
};
ko.bindingHandlers.uniqueFor = {
init: function(element, valueAccessor) {
var after = ko.bindingHandlers.uniqueId.counter + (ko.utils.unwrapObservable(valueAccessor()) === "after" ? 0 : 1);
element.setAttribute("for", ko.bindingHandlers.uniqueId.prefix + after);
}
};
Вы бы использовали их как:
<ul data-bind="foreach: items">
<li>
<label data-bind="uniqueFor: 'before'">Before</label>
<input data-bind="uniqueId: true, value: name" />
<label data-bind="uniqueFor: 'after'">After</label>
</li>
</ul>
Итак, он просто сохраняет состояние привязки, увеличивая ko.bindingHandlers.uniqueId.counter
. Затем привязка uniqueFor
просто должна знать, есть ли перед или после поля знать, как получить правильный идентификатор.
Пример здесь: http://jsfiddle.net/rniemeyer/8KJD3/
Если ваши метки не были рядом с их полями (несколько вводов, привязанных к каждой метке, возможно, в отдельных строках таблицы), вам нужно будет посмотреть на другую стратегию.
Ответ 3
Я не могу ответить на выбранный ответ, но у меня есть расширенная версия кода, который поддерживает несколько уникальных идентификаторов для каждого входного значения. Это в моем блоге на http://drewp.quickwitretort.com/2012/09/18/0 и повторяется здесь:
ko.bindingHandlers.uniqueId = {
/*
data-bind="uniqueId: $data" to stick a new id on $data and
use it as the html id of the element.
data-which="foo" (optional) adds foo to the id, to separate
it from other ids made from this same $data.
*/
counter: 0,
_ensureId: function (value, element) {
if (value.id === undefined) {
value.id = "elem" + (++ko.bindingHandlers.uniqueId.counter);
}
var id = value.id, which = element.getAttribute("data-which");
if (which) {
id += "-" + which;
}
return id;
},
init: function(element, valueAccessor) {
var value = valueAccessor();
element.id = ko.bindingHandlers.uniqueId._ensureId(value, element);
},
};
ko.bindingHandlers.uniqueFor = {
/*
data-bind="uniqueFor: $data" works like uniqueId above, and
adds a for="the-new-id" attr to this element.
data-which="foo" (optional) works like it does with uniqueId.
*/
init: function(element, valueAccessor) {
element.setAttribute(
"for", ko.bindingHandlers.uniqueId._ensureId(valueAccessor(), element));
}
};
Теперь у вас может быть несколько отмеченных флажков для одной записи с автоматическими идентификаторами:
<li data-bind="foreach: channel">
<input type="checkbox" data-which="mute" data-bind="uniqueId: $data, checked: mute">
<label data-which="mute" data-bind="uniqueFor: $data">Mute</label>
<input type="checkbox" data-which="solo" data-bind="uniqueId: $data, checked: solo">
<label data-which="solo" data-bind="uniqueFor: $data">Solo</label>
</li>