Limit foreach loop knockout

12,400

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

Share:
12,400

Related videos on Youtube

EntryLevel
Author by

EntryLevel

Updated on June 04, 2022

Comments

  • EntryLevel
    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.

  • EntryLevel
    EntryLevel almost 11 years
    can you add some context to this . Also it is not the whole array I would like to limit just the sub-array
  • Rick Klaassen
    Rick Klaassen almost 11 years
    Then do this just for the sub-array. replace data with data.Attributes
  • Andrey Nelubin
    Andrey Nelubin almost 11 years
    I think you mean $index(). $index < 3 will always return false jsfiddle.net/aDahT/1087
  • Piotr Stulinski
    Piotr Stulinski about 10 years
    This 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
    mbx-mbx over 9 years
    Wouldn'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
    Mark Carpenter Jr over 6 years
    I 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.