Pass $index into foreach template binding

1,409 views
Skip to first unread message

Rupert Bates

unread,
Jan 20, 2011, 6:43:44 AM1/20/11
to KnockoutJS
Hi, from what I can see this isn't currently possible but it would be
very useful if the foreach template binding could pass an $index value
into the template as well as $data.

I am currently having to use {{each(i, item) myArray()}} inside my
template since I need this index value, but this results in the whole
list being re-rendered on each change, thereby losing temporary state
(particularly any file which the user has selected in a file upload
box).

I would love to hear that I'm wrong and there is a way to achieve
this, but if not could you consider making this change please?

Knockout is a fantastic library, thanks for all your hard work,
Rupert

Matt

unread,
Jan 21, 2011, 2:14:48 AM1/21/11
to KnockoutJS
On Jan 20, 4:43 am, Rupert Bates <rupert.ba...@gmail.com> wrote:
> Hi, from what I can see this isn't currently possible but it would be
> very useful if the foreach template binding could pass an $index value
> into the template as well as $data.

I would find this very useful as well. I had posted another message on
the forum before I realized this request was already submitted.

Matt

abp

unread,
Jan 21, 2011, 4:42:10 AM1/21/11
to KnockoutJS
You can use multiple solutions for this, as long as it isn't
implemented in knockout.
I use, for example a uniqueId bindingHandler thats based on the
uniqueName binding of knockout.

ko.bindingHandlers.uniqueId = {
'init': function (element, valueAccessor) {
if (valueAccessor()) {
element.id = "ko_unique_" + (+
+ko.bindingHandlers['uniqueId'].currentIndex);
}
}
};
ko.bindingHandlers.uniqueId.currentIndex = 0;

And:
<input type="text" data-bind="uniqueId: true">

Also a solution:
var ViewModel = function()
{
this.index = (function() {
var counter = 0;
return function() { return counter++; };
})();
}

On your view Model and something like:
<label data-bind="text: viewModel.index()"></label>

fla...@gmail.com

unread,
Jan 23, 2011, 12:12:37 PM1/23/11
to KnockoutJS
Hi all

Abp's solution will work well if you want the index as a way to
generate a unique ID.

If you actually just want the numerical position of the item in your
array, the reason KO doesn't already provide an index is that it
frequently would be wrong to do so. The whole idea of the "foreach"
binding is to let you modify your array later and only rerender the
templates for the new ones you've added. If your templates depend on
knowing the index of the item they're rendering, how does this make
sense when items are inserted or deleted later (effectively changing
the numerical positions of the output) and those templates aren't
rerendered?

If you know you're not going to be inserting or removing things later,
you'll get the same result by not using "foreach" outside your
template, but instead using a {{foreach}} loop *inside* your template,
which will give you the index and value of each item in your array.

Does this make sense, or have I misunderstood your request? If there's
a use case where you think this would definitely make sense and it
isn't already covered, please let us know!

Thanks
Steve

Rupert Bates

unread,
Jan 25, 2011, 5:17:50 AM1/25/11
to knock...@googlegroups.com
Hi, thanks for the responses. What I want is the actual numerical position of the item in the array and the reason I want it is that I create input names using it to facilitate model binding in asp.net mvc on the server.
eg.

<input type="text" name="Microsite.PresentationGroups[0].Title" ...

I think the way it could work is as follows:

Where a change is made to the array that would not affect the index of previously rendered items such as adding a new item to the end of the array or deleting an item from the end of the array, then those items would remain as they are and not be re-rendered.

However if a change affected the index of an item eg. deleting the second to last item would affect the index of the item that comes after it, then all affected items would be re-rendered.

I appreciate that this could add significantly to the complexity of the conditional rendering code, but perhaps you could consider it?

In the meantime I think what I will probably do is to switch the way my site works to a json post to update so that I don't need the input names for binding any more.
Cheers,
Rupert

Matt

unread,
Jan 26, 2011, 12:06:13 AM1/26/11
to KnockoutJS
On Jan 23, 10:12 am, "st...@codeville.net" <fla...@gmail.com> wrote:
> If you actually just want the numerical position of the item in your
> array, the reason KO doesn't already provide an index is that it
> frequently would be wrong to do so. The whole idea of the "foreach"
> binding is to let you modify your array later and only rerender the
> templates for the new ones you've added.

Oh yeah, good point. For the spot I wanted it, I was adding and
removing items from the end, but never re-ordering them, so I didn't
think through the fact that the index might change. Now I understand
why that isn't part of the built-in behavior. I can work around it
with {{each}} or changing my viewModel.

Thanks,

Matt
Reply all
Reply to author
Forward
0 new messages