neverok
neverok

Reputation: 13

knockout js foreach loop context

A have template:

<table data-bind="foreach: {data: messages, as: 'message'}">
    <tr>   <td data-bind="text: $root.go"></td>   </tr>
</table>

My js:

function CreateViewModel() {
    var self = this;
    this.go = ko.computed(function() {
        return 'not really important what here for now';
    });
}

ko.applyBindings( new MessagesVM );

The context in 'go' while runtime is 'window', I want it to be the current item. Is it possible? If I change tmpl line to "<td data-bind="text: go"></td>" I will have an error ['go' is not defined].

Upvotes: 0

Views: 721

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075855

The context in 'go' while runtime is 'window', I want it to be the current item. Is it possible?

I don't think you can with a computed, no. If you make it a simple function you can: data-bind="text: $root.go.call(message)" (generically it would be .call($data), but since you're naming it in your foreach):

function MessagesVM() {
  var self = this;
  this.messages = ko.observableArray([
    "Message 1",
    "Message 2"
  ]);
  this.go = function() {
    return 'this is: "' + this + '"';
  };
}

ko.applyBindings(new MessagesVM);
<table data-bind="foreach: {data: messages, as: 'message'}">
  <tr>
    <td data-bind="text: $root.go.call(message)"></td>
  </tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>


Alternately, if you want some of the functionality of a computed but acting on the individual item, I'd probably use a binding handler:

ko.bindingHandlers.specialText = {
  update: function(element, valueAccessor) {
    var value = ko.unwrap(valueAccessor());
    element.innerHTML = "'value' is " + value;
  }
};
function MessagesVM() {
  var self = this;
  this.messages = ko.observableArray([
    "Message 1",
    "Message 2"
  ]);
}

ko.applyBindings(new MessagesVM);
<table data-bind="foreach: {data: messages, as: 'message'}">
  <tr>
    <td data-bind="specialText: message"></td>
  </tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

Upvotes: 1

Roy J
Roy J

Reputation: 43899

Turn it into a function and pass the context in. As a computed, it has a cached value and so cannot be context-dependent.

function CreateViewModel() {
  var self = this;
  this.messages = [
    'one',
    'two'
  ];
  this.go = function(data) {
    var context = data;
    return 'Context:' + context;
  };
}

ko.applyBindings(new CreateViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table data-bind="foreach: {data: messages, as: 'message'}">
  <tr>
    <td data-bind="text: $root.go(message)"></td>
  </tr>
</table>

Upvotes: 1

Related Questions