Skip to content

foreach: $array, $isFirst, $isLast, $isEven, $isOdd, $next, $previous - #2415

Open
barsh wants to merge 1 commit into
knockout:masterfrom
barsh:foreach
Open

foreach: $array, $isFirst, $isLast, $isEven, $isOdd, $next, $previous#2415
barsh wants to merge 1 commit into
knockout:masterfrom
barsh:foreach

Conversation

@barsh

@barsh barsh commented Sep 18, 2018

Copy link
Copy Markdown

There are situations where you want access to the array bound to a foreach, for example:

var vm = {
  array: [0,1,2,3]
}
ko.applyBindings(vm)
<div data-bind="foreach: array.filter(el => el > 1)">
    <div>
        <span data-bind="text: $index"></span> 
        of 
        <span data-bind="text: $parent.array.length-1"></span>
    </div>
</div>

This produces

0 of 3
1 of 3

instead of

0 of 1
1 of 1

This PR introduces $array to the context, for example:

<div data-bind="foreach: array.filter(el => el > 1)">
    <div>
        <span data-bind="text: $index"></span> 
        of 
        <span data-bind="text: $array.length-1"></span>
    </div>
</div>

Which produces the desired result of

0 of 1
1 of 1

In addition to $array the following additional helpers were added:
$isFirst, $isLast, $isEven, $isOdd, $next, $previous

For example:

var vm = {
  array: ['a', 'b']
}
ko.applyBindings(vm)
<div data-bind="foreach: array">
    <div>$data: <span data-bind="text: $data"></span></div>
    <div>$isFirst: <span data-bind="text: $isFirst"></span></div>
    <div>$isLast: <span data-bind="text: $isLast"></span></div>
    <div>$isEven: <span data-bind="text: $isEven"></span></div>
    <div>$isOdd: <span data-bind="text: $isOdd"></span></div>
    <div>$previous: <span data-bind="text: $previous"></span></div>
    <div>$next: <span data-bind="text: $next"></span></div>
    <hr>
</div>

produces:

$data: a
$isFirst: true
$isLast: false
$isEven: true
$isOdd: false
$previous:
$next: b
----
$data: b
$isFirst: false
$isLast: true
$isEven: false
$isOdd: true
$previous: a
$next:

@brianmhunt

Copy link
Copy Markdown
Member

Conceptually I think this is useful, but we'd need to make sure it's consistent with the fast-foreach API in tko (since folks may move back-and-forth); the tko API is more complex since it's O(c) until $index is accessed, then it becomes O(1). A similar set of accessors could add quite a bit of complexity to fast-foreach.

@knockout knockout deleted a comment from allen2143 Sep 25, 2018
@bikeshedder

Copy link
Copy Markdown

I wonder why you want to perform such code in the template rather than adding a filteredArray to your model:

model.filteredArray = ko.pureComputed(() => model.array.filter(el => el > 1))

I don't see a huge downside of adding an $array to the context inside a forEach but I wonder if this kind of code isn't bad practice and should be avoided anyways.

@barsh

barsh commented Oct 3, 2018

Copy link
Copy Markdown
Author

@bikeshedder: Thanks for jumping in with a great point that I agree with. However, the situation that inspired this pull request was a bit more complex than the simplified example provided here and while your suggestion could be applied to that situation too, filtering in the template itself in that particular case led to a more developer-friendly solution.

@caseyWebb

caseyWebb commented Oct 19, 2018

Copy link
Copy Markdown
Contributor

I just realized that with the addition of the let binding in 3.5.0-rc, the need for $array is greatly reduced.

For example...

<!-- ko let: { myFilteredArray: arr().filter(...) } -->
<ul data-bind="foreach: myFilteredArray">
  <!-- myFilteredArray is still in scope -->
</ul>
<!-- /ko --> 

(I haven't actually tested this exact code)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants