I need an index in a foreach binding....how?

162 views
Skip to first unread message

Scott Messinger

unread,
Jul 26, 2011, 9:24:14 AM7/26/11
to knock...@googlegroups.com
Hi,

This topic has been brought up before but no one has provided an answer. 

The Task
I'm creating a sortable list

Situation:
I'm using a nested template and rpn's awesome jqueryui sortable binding. 
<ol data-bind="template :{name : 'show_unit', foreach: unit().units , sortableList: unit().units}">

Problem
I want to style the position number of each element. It's been nearly impossible to get the look I want trying to format the li tag. 

Goal:
I want to be able to do this:
<span data-bind="text: $index"></span>

Any ideas how?

I should note that using the {{each ... }} won't work as I won't be able to use the jqueryui binding that rpn made. Further, if I use {{each ... }} I think I'll have to do unnecessary refreshes. when I update the list.

Thanks in advance!
Scott

rpn

unread,
Jul 26, 2011, 9:50:29 AM7/26/11
to knock...@googlegroups.com
Hey Scott-
Two ideas for you:

If you don't mind using {{each}} you can modify the sortableList binding to read the item off of some meta-data stored with the element rather than using the tmplItem() function.  Then, you put a sortableItem binding on the individual items that attaches the meta-data.  This is similar to how the other binding works in the post where you can drop items from one array to another.  Sample: http://jsfiddle.net/rniemeyer/ywanw/

If you don't want to use {{each}}, because you don't want your entire template to re-render constantly, then you could put an "index" property on your individual items.  Rather than make it a dependentObservable on each item where re-evaluating would require looping through the array and finding your own index, I would probably do a manual subscription on the observableArray and make one loop through the items and update the "index" property.  Might look like this:

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

You could obviously call the observable whatever you want, if index might cause a conflict.  The nice part about this is that you don't really have to mess with your item definition at all, as this index is just managed completely from this subscription.  You would want to set up this subscription prior to adding any items to your array.  Sample here:  http://jsfiddle.net/rniemeyer/CXBFN/

Scott Messinger

unread,
Jul 26, 2011, 10:08:22 AM7/26/11
to knock...@googlegroups.com
Thanks, Ryan! That's helpful!

I'm going to post another question...but, in short, I think I have a bigger problem: the whole template is taking 15-20 seconds! to render on IE7.

-Scott

Scott Messinger

unread,
Jul 26, 2011, 10:33:33 AM7/26/11
to knock...@googlegroups.com
I hate IE.

It turns out that IE just got remarkably slow because I had left it open for a while. Ridiculous. It actually takes 0.7 seconds to render after I closed it down and reopened it. 

-Scott

Scott Messinger

unread,
Jul 26, 2011, 12:12:40 PM7/26/11
to knock...@googlegroups.com
Hi Ryan,

I'm having a hard time figuring out where to apply the subscription. I've pasted the code I'm using into the gist:


Any ideas?

-Scott

rpn

unread,
Jul 26, 2011, 12:20:48 PM7/26/11
to knock...@googlegroups.com
So, what does the code look like that sets up the units observableArray?   You should see your console.log any time that you modify the observableArray (push a unit to it, or simply set the value to an array of units).

Scott Messinger

unread,
Jul 26, 2011, 12:32:10 PM7/26/11
to knock...@googlegroups.com
Thanks for the quick response!

I've updated the gist with the code that creates the units observableArray. In short, I use the mapping plugin.

rpn

unread,
Jul 26, 2011, 12:55:43 PM7/26/11
to knock...@googlegroups.com
If I follow your code correctly, then I think that maybe the easiest choice is to set up the subscription where you have it and then immediately call viewModel.units.valueHasMutated() .  This will force the subscription to run now.


Scott Messinger

unread,
Jul 26, 2011, 1:01:37 PM7/26/11
to knock...@googlegroups.com
You're amazing! Thank you so much!

-Scott
Reply all
Reply to author
Forward
0 new messages