template knockoutjs knockout for define data component knockout.js client-side-templating

knockout.js - for - knockoutjs component



Identificadores Ășnicos en plantillas de knockout.js (3)

Supongamos que tengo una plantilla knockout.js como esta:

<script type="text/html" id="mytemplate"> <label for="inputId">Label for input</label> <input type="text" id="inputId" data-bind="value: inputValue"/> </script>

Si represento esta plantilla en varios lugares de la página, termino con varias entradas con la misma identificación (y varias etiquetas con el mismo valor), lo que tiene malas consecuencias. En particular, todos los códigos que dependen de los identificadores pueden no funcionar correctamente (en mi caso, uso el complemento jquery.infieldlabel que se confunde con varias entradas con el mismo ID). La forma en que resuelvo este problema ahora es agregar un atributo único de identificación al modelo que ato a la plantilla:

<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>

Esto funciona, pero no es muy elegante ya que tengo que tener este atributo de identificación artificial en mis modelos que no se usa para nada más. Me pregunto si hay una mejor solución aquí.


He hecho algo como esto en el pasado:

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); } };

Los usarías como:

<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>

Por lo tanto, solo mantiene el estado en el enlace incrementando ko.bindingHandlers.uniqueId.counter . Entonces, el enlace único uniqueFor enlace solo necesita saber si es antes o después del campo para saber cómo obtener la identificación correcta.

Muestra aquí: http://jsfiddle.net/rniemeyer/8KJD3/

Si sus etiquetas no estaban cerca de sus campos (múltiples entradas vinculadas antes de cada etiqueta, tal vez en filas separadas de una tabla), entonces tendría que mirar una estrategia diferente.


No puedo responder a la respuesta seleccionada, pero tengo una versión mejorada del código que admite múltiples identificadores únicos por valor de entrada. Está en mi blog en http://drewp.quickwitretort.com/2012/09/18/0 y se repite aquí:

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)); } };

Ahora puede tener múltiples casillas marcadas para un registro con identificadores automáticos:

<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>


Una alternativa que no depende del orden en el que los campos están vinculados es que el conjunto de enlaces tenga una propiedad de id en los datos, lo que debería ser observable.

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); } };

Lo usarías como:

<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>

Muestra: http://jsfiddle.net/rniemeyer/JjBhY/

Lo bueno de agregar una propiedad a la función observable es que cuando la conviertes en JSON para enviar de vuelta al servidor, simplemente desaparecerá de manera natural, ya que lo observable simplemente se convertirá en su valor desenvuelto.