2

I populate a list from an array using KnockoutJS:

<div data-bind:"foreach: list">
   <input type="text" data-bind="value: myText" />
</div>

function ViewModel() {
    self.list = ko.observableArray([
        new listItem("sample text")
    ]);
};

function listItem (text) {
    this.myText = text;
};

I can assign an id to the individual instances of my input like so

<input data-bind="attr: { id: $index } ...

How do I access this index from within my listItem function? I want to be able to do something like

function listItem (text) {
    this.myText = text;
    this.index = $index;
};

in order to use this for further processing.

1 Answer 1

14

You could create a custom binding that sets your property to the index, it would look something like:

ko.bindingHandlers.setIndex = {
    init: function(element, valueAccessor, allBindings, data, context) {
        var prop = valueAccessor();
        data[prop] = context.$index;
    }        
};

This assumes that you are dealing with objects in your array. You would use it like:

<ul data-bind="foreach: items">
    <li data-bind="setIndex: 'myIndex', text: name"></li>
</ul>

So, this copies the $index observable on to your object with the property name that you specify. Sample: http://jsfiddle.net/rniemeyer/zGmcg/

Another way that you can do this outside of bindings (this is how I used to do it before $index) is to subscribe to changes to the observableArray and repopulate an index each time.

Here is what an extension to an observableArray might look like:

//track an index on items in an observableArray
ko.observableArray.fn.indexed = function(prop) {
    prop = prop || 'index';
   //whenever the array changes, make one loop to update the index on each
   this.subscribe(function(newValue) {
       if (newValue) {
           var item;
           for (var i = 0, j = newValue.length; i < j; i++) {
               item = newValue[i];
               if (!ko.isObservable(item[prop])) {
                  item[prop] = ko.observable();
               }
               item[prop](i);      
           }
       }   
   }); 

   //initialize the index
   this.valueHasMutated(); 
   return this;
};

You would then use it like:

this.myItems = ko.observableArray().indexed('myIndexProp');

Here is a sample: http://jsfiddle.net/rniemeyer/bQD2C/

Sign up to request clarification or add additional context in comments.

3 Comments

Many thanks for the prompt and detailed answer, especially for the jsfiddles! In trying out the first option with the custom binding, I noticed that 'myIndex' is accessible for a data binding, yet I cannot access it within the javascript template for the list item during the initial population of the array. Any pointers as to why this is the case?
In that case, the index would not get set onto your object until after bindings are applied. So, any code that runs before bindings are applied would not have it available. If you need it before that, then you might want to consider the other option.
Thanks. For now, the first option should be fine, but I'll keep this in mind should I need earlier access.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.