Limit foreach loop knockout
Solution 1
If you always have 3 attributes and you always only want to show 2 of them, you don't need to foreach them, exactly.
However, there is the special binding context variable $index()
, which will let you do some basic hiding, although it wouldn't prevent rendering. Because $index is 0-based, the condition is $index() < 2
. As Andrey points out in the comments, $index
is an observable, so you have to call it with parentheses as a method, or comparisons won't do what you expect (you'll be comparing an int against a function).
<ul data-bind="foreach: survey.Attributes">
<li data-bind="visible: $index() < 2">
Name: <span data-bind="text: Name"> </span><br/>
Type: <span data-bind="text: Type"> </span><br/>
Year: <span data-bind="text: Year"> </span><br/>
</li>
</ul>
If you want a generic limiter on a foreach loop, you're right, it's not simple. You would have to make a custom binding.
Another approach you could consider is pre-processing your data in the viewmodel. When you set this.survey = data;
, you could remove any of the attributes you don't want to display at that point.
Edit: I see from your edit that you know about the ko: if
psuedo-elements. I completely forgot about those, but you could easily use one to prevent rendering template items beyond a certain index. The foreach
would still evaluate the observable, that should not have any kind of huge overhead by itself.
Solution 2
JavaScript arrays include the excellent slice
method that should fill your need nicely:
template: { name: 'photo-template', foreach: Attributes.slice(0,2) }
But as @Patrick-M mentioned, you don't need a loop:
template: { name: 'photo-template', data: Attributes[0] }
template: { name: 'photo-template', data: Attributes[1] }
My Repeat binding includes an option for limiting the number of repetitions:
<div data-bind="repeat: { foreach: Attributes, count: 2 }"
data-repeat-bind="template: { name: 'photo-template', data: $item() }">
</div>
Solution 3
You can create computed with limited array limit:
var limited = ko.computed( function() {
return Attributes.slice(0, limit);
});
Then all you have to do to foreach limited. You can even add somekind of "more" element:
<!-- ko if: Attributes().length > limit -->
<div class="more">...</div>
<!--/ko-->
I hope it will be helpful for further generations ;)
Solution 4
Put the .slice(0,1) to get first 2 items from your FOREACH data-bind array (model).
.slice(0,1)
Something like this
<!-- ko foreach: { data: items().slice(0, 1) } -->
<div>
Your html here!
</div>
<!-- /ko -->
Or if you prefer
<tbody data-bind="foreach: people.slice(0, 1)">
<tr>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
</tr>
</tbody>
About slice() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
Related videos on Youtube
EntryLevel
Updated on June 04, 2022Comments
-
EntryLevel almost 2 years
I have this knockout mapping my array retreived from the ajax call below.
function InvoiceViewModel(data) { var self = this; self.survey = data; }
Ajax Call
$.ajax({ url: 'http://localhost:43043/api/damage', type: 'GET', headers: { 'Accept': 'application/json' }, data: { orderNumber: num, category: cat }, success: function (data) { var usingRoutData = document.URL; ko.applyBindings(new InvoiceViewModel(data)); }, error: function () { alert('failure'); } });
My Array
var test = { Name: Blah, Attributes: [ {Name: Test, Type: Photo, Year:1988}, {Name: Test, Type: Photo, Year:1988}, {Name: Test, Type: Photo, Year:1988} ] };
How I am binding my data
<div id="invoiceBodyWrapper"> <div data-bind="template: { name: 'invoice-template', foreach: surveys }"> </div>
<div class="invoiceWrapper"> </div> <div id="completePictureWrapper" data-bind="template: { name: 'photo-template', foreach: new Array(Attributes) }"></div> </div> </script> <script type="text/html" id="photo-template"> <!-- ko if: classification === 'photo' --> <div id="pictureWrappers"> <img class="img" data-bind="attr: { src: 'http://myimagepath/download/full/' + $index()+1 }" /> </div> <!-- /ko --> </script> <script src="~/Scripts/DamageInvoiceCreation.js"></script>
I need a way to limit my attributes foreach loop to only show 2 of the 3 attributes. I have only found a few things on how to do this and they seem very overly complicated. I cannot imagine there is not a simple way to do this in knockout.
-
Dustin Kingen almost 11 years
-
-
EntryLevel almost 11 yearscan you add some context to this . Also it is not the whole array I would like to limit just the sub-array
-
Rick Klaassen almost 11 yearsThen do this just for the sub-array. replace data with data.Attributes
-
Andrey Nelubin almost 11 years
-
Piotr Stulinski about 10 yearsThis is by far one of the nicest solutions to this problem, if you only want to show 2 attributes you should use < 2 [0 is also a number :)]
-
mbx-mbx over 9 yearsWouldn't it be better to use the "if" binding rather than the "visible"? The visible binding will still render the elements but just hide them so you could be cluttering up the DOM with unwanted items. A cleaner way may also be to use the knockout repeat plugin: github.com/mbest/knockout-repeat/blob/master/README.md
-
Mark Carpenter Jr over 6 yearsI think sometimes people get "too ambitious" with KO, and throw too much JS in the HTML. This is a pretty logical solution. Don't get me wrong there is nothing wrong with KO, it's a great UI lib.