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.
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With