Reputation: 4067
I am having a problem with nested foreach statements in KO.
The following statements work fine on their own but when I combine them the inner one doesn't work and doesn't throw an error either.
Ideally I would like the inner join to be conditional but I have tried it without the if clause and still no luck.
Experts have a property array of all ExpertRoles assigned to the particular user. The outer loop is meant to print all ExpertRoles and the inner loop is meant to print the Experts that match the Expert role of the outer loop
1st foreach: WORKS
<ul data-bind="foreach: ExpertRoles">
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': Id }, text: Name">
</li>
</ul>
2nd foreach: WORKS
<ul data-bind="foreach: Experts">
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': ConnectionId, 'title': Username }">
<a href="javascript:void(0);" data-bind="text: Username"></a>
</li>
</ul>
combined with if clause: inner loop doesn't work, throws no error
<ul data-bind="foreach: ExpertRoles">
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': Id }, text: Name">
<ul data-bind="foreach: Experts">
<li class="expert ui-menu-item" role="menuitem" data-bind="if: ExpertRoles.indexOf($parent.Id) > 0, attr: { 'data-cid': ConnectionId, 'title': Username }">
<a href="javascript:void(0);" data-bind="text: Username"></a>
</li>
</ul>
</li>
</ul>
no if clause: inner loop doesn't work, throws no error
<ul data-bind="foreach: ExpertRoles">
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': Id }, text: Name">
<ul data-bind="foreach: Experts">
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': ConnectionId, 'title': Username }">
<a href="javascript:void(0);" data-bind="text: Username"></a>
</li>
</ul>
</li>
</ul>
EDIT:
Let me add that I have tried container-less syntax (<!-- ko foreach:, <!-- ko if:
, etc)
This is a part of my viewmodel, the data gets updated by SignalR so it is difficult for me to make a fiddle. Please, focus on that they work on their own.
var viewModel = {
Users: ko.mapping.fromJS([]),
ExpertRoles: ko.mapping.fromJS([])
};
// experts are a subset of users
viewModel.Experts = ko.computed(function () {
return ko.utils.arrayFilter(this.Users(), function (item) {
return item.IsExpert() === true;
});
}, viewModel);
EDIT 2:
With your help I have managed to make the loops work. For some reason binding Name
on the outer li
causes the inner loop to stop working.
<ul>
<!-- ko foreach: $root.ExpertRoles -->
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': Id }">
<span data-bind="text: Name"></span>
<ul>
<!-- ko foreach: $root.Experts -->
<li class="expert ui-menu-item" role="menuitem" data-bind="attr: { 'data-cid': ConnectionId }">
<a href="javascript:void(0);" data-bind="text: Username"></a>
</li>
<!-- /ko -->
</ul>
</li>
<!-- /ko -->
</ul>
Upvotes: 0
Views: 450
Reputation: 6209
When you are using foreach
in a nested loop you can access only properties of a parent model. Experts
is not a property of an item from the ExpertRoles
. It is a property of viewModel
model. That's why it doesn't work. Try using $root
:
<ul data-bind="foreach: $root.Experts">
<li class="expert ui-menu-item" role="menuitem" data-bind="if: $root.ExpertRoles.indexOf($parent.Id) >= 0, attr: { 'data-cid': ConnectionId, 'title': Username }">
<a href="javascript:void(0);" data-bind="text: Username"></a>
</li>
</ul>
Upvotes: 1