Привязать элемент шаблона к индексу массива в KnockoutJS

Мне нужно указать <div>'s в шаблоне KnockoutJS в соответствии с положением, которое они отображаются в списке для JQuery-плагина, например:

<div id="item1">...</div>
<div id="item2">...</div>
<div id="item3">...</div>

Есть ли способ привязки к индексу элемента в массиве с помощью KnockoutJS? Было бы позором, если бы мне пришлось добавлять эти данные к выбору в базе данных с помощью ROWINDEX.

Ответ 1

update: KO теперь поддерживает контекстную переменную $index, которую вы можете использовать в параметре foreach (или template с параметром foreach). Документы: http://knockoutjs.com/documentation/binding-context.html

Если у вас все в порядке с использованием шаблонов jQuery {{each}}, то что-то вроде этого будет работать:

<div data-bind="template: 'allItemsTmpl'"></div>
<script id="allItemsTmpl" type="text/html">
    {{each(i, item) items}}
    <div data-bind="attr: { id: 'item' + i }">
        <input data-bind="value: name" />
    </div>
    {{/each}}
</script>

Если вам нужно было использовать параметр foreach, то что-то вроде этого будет работать:

<div data-bind="template: { name: 'itemTmpl', foreach: items }"></div>
<button data-bind="click: addItem">Add Item</button>
<script id="itemTmpl" type="text/html">
    <div data-bind="attr: { id: 'item' + ko.utils.arrayIndexOf(viewModel.items, $data) }">
        <input data-bind="value: name" />
    </div>
</script> 

Изменить: в наши дни я предпочитаю создавать подписку на мой наблюдаемый массив, который должен пройти один проход через массив и создать/установить индекс, наблюдаемый в элементе. Например:

//attach index to items whenever array changes
viewModel.tasks.subscribe(function() {
    var tasks = this.tasks();
    for (var i = 0, j = tasks.length; i < j; i++) {
       var task = tasks[i];
        if (!task.index) {
           task.index = ko.observable(i);  
        } else {
           task.index(i);   
        }
    }
}, viewModel);

Пример здесь: http://jsfiddle.net/rniemeyer/CXBFN/

или вы можете принять эту идею и расширить наблюдаемые массивы, чтобы обеспечить функцию indexed, которая позволит вам установить это, просто позвонив myObservableArray.indexed().

Вот пример: http://jsfiddle.net/rniemeyer/nEgqY/

Ответ 2

Это намного проще с Knockout 2.1.0 и выше:

<div data-bind="foreach: items">
   <div data-bind="attr: { id : 'item' + $index() }"></div>
</div>

Необязательных сценариев не требуется.

Предупреждение: attr: { id : 'item' + $index } НЕ будет работать. Поскольку $index является самой функцией, невозможность добавления скобок приведет к тому, что ваш id будет конкатенацией "элемента" и определения всей функции!

Ответ 3

Имела ту же проблему, но с условием dependantObservable в качестве источника для шаблона foreach, увы, ko.utils.arrayIndexOf не работает...

Решение: создала функцию, которая воссоздала массив, возвращаемый функцией dependantObservable, и просто использовал функцию js indexOf с Item:

arrayIndexDO: function (item) {
    var filteredArray = ko.utils.arrayFilter(viewModel.someObservableArray(), function (element) {
        return element.id() == view.selectedId();
    });
    var index = filteredArray.indexOf(item);
    return index;
}

Et voilà, ожидаемый индекс в jQuery tpl.