Уникальные идентификаторы в шаблонах knockout.js - PullRequest
23 голосов
/ 10 февраля 2012

Предположим, у меня есть шаблон 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, который больше ни для чего не используется. Интересно, есть ли здесь лучшее решение?

Ответы [ 3 ]

62 голосов
/ 11 февраля 2012

Альтернатива, которая не зависит от порядка привязки полей, заключается в том, чтобы привязка установила свойство 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 для отправки обратно на сервер, тогда он просто естественным образом исчезнет, ​​поскольку наблюдаемое просто превратится в развернутое значение.

6 голосов
/ 10 февраля 2012

Я делал что-то подобное в прошлом:

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 голосов
/ 19 сентября 2012

Я не могу ответить на выбранный ответ, но у меня есть расширенная версия кода, которая поддерживает несколько уникальных идентификаторов на входное значение.Это в моем блоге на 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>
...