web-dev-qa-db-fra.com

Knockout.js - Foreach binding - Test si dernier élément

J'utilise le modèle suivant:

<div class="datatypeOptions" data-bind="if: $data.datatypeTemplate().allowOptions">
    <h3>Allowed responses</h3>

    <p data-bind="if: $data.datatypeTemplate().datatypeOptions().length == 0">There are no responses for this question, yet. <a href="#" data-bind="click: function(d, e){$root.addDatatypeOption($data.datatypeTemplate());}">Add one</a>
    <ul data-bind="foreach: $data.datatypeTemplate().datatypeOptions()">
        <li>
            <a href="#" data-bind="text: name, click: $root.selectedDatatypeOption, visible: $data !== $root.selectedDatatypeOption()"></a>
            <input data-bind="value: name, visibleAndSelect: $data === $root.selectedDatatypeOption(), event: { blur: $root.clearDatatypeOption }, executeOnEnter: { callback: function(){ $root.addDatatypeOption($parent.datatypeTemplate()); } }" />
            //I want to show this a tag only if $data is the last element in the array.
Problem here ===>  <a href="#" data-bind="if: $data == $parent.datatypeTemplate().datatypeOptions()[ $parent.datatypeTemplate().datatypeOptions().length - 1 ], click: function(d, e){$root.addDatatypeOption($data.datatypeTemplate());}"><img src='/static/img/icons/custom-task-wizard/black/plus_12x12.png' title='Add option'></a>
        </li>
    </ul>
</div>

Je reçois cette erreur dans la console:

Uncaught Error: Unable to parse bindings.
Message: TypeError: Object [object Object] has no method 'datatypeTemplate';
Bindings value: if: $data == $parent.datatypeTemplate().datatypeOptions()[ $parent.datatypeTemplate().datatypeOptions().length - 1 ], click: function(d, e){$root.addDatatypeOption($data.datatypeTemplate());}

Est-ce que ma seule option pour ajouter une fonction à mon modèle de vue qui renvoie vrai/faux si l'élément passé est le dernier dans le tableau?

40
Jonathan

J'ai simplifié le problème, mais ce jsFiddle montre une solution possible.

"if" contraignant:

<div data-bind="if: ($index() === ($parent.data().length - 1))">I'm the last element</div>

Containerless "if" contraignant:

<!-- ko if: ($index() === ($parent.data().length - 1)) -->
<div>I'm the last element again</div>
<!-- /ko -->

Vous pouvez utiliser $index dans une liaison foreach pour obtenir l'index de l'élément actuellement lié. Utilisez cela pour comparer avec la collection originale du parent.

Voir HERE pour plus d'informations sur les contextes de liaison.

77
Brett Postin

J'ai remarqué qu'il y avait un certain nombre de demandes d'améliorations de KO afin de prendre en charge les propriétés réservées $length, $last ou $array dans la liaison foreach bien que, pour diverses raisons (souvent les performances), elles ne l'aient pas intégré à la base de code.

Si quelqu'un souhaite exposer ces éléments pour un cas spécifique à l'aide d'une liaison personnalisée, voici un exemple simple d'exposition de la variable $length afin d'imprimer une "jolie" liste. 
Vous utilisez simplement forEachWithLength partout où vous utiliseriez normalement foreach.

ko.bindingHandlers.forEachWithLength = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, context)
    {         
        return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, context);
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, context) 
    {         
        var array = ko.utils.unwrapObservable(valueAccessor());
        var extendedContext = context.extend({"$length" : array.length });
        ko.bindingHandlers.foreach.update(element, valueAccessor, allBindingsAccessor, viewModel, extendedContext);   
    }
};

Exemple d'utilisation:

<div data-bind="forEachWithLength: myArray">
    <span data-bind="text: $data"></span>
    <!-- ko if: ($index() < $length-2) -->, <!-- /ko -->
    <!-- ko if: ($index() === $length-2) --> and <!-- /ko -->
</div>

Entrée:["One", "Two", "Three", "Four"]

Sortie:One, Two, Three and Four

Fiddle

Lectures supplémentaires

9
mwielbut

Si vous utilisez NOT en utilisant l'option as dans la liaison foreach, passez à la réponse la plus votée de cette question

Si vous FAITES, utilisez l'opérateur as dans la liaison foreach. alors cette réponse ne fonctionnera pas PAS.

Voici la solution dans ce cas

<div data-bind="foreach:{data: Items, as :'item'}">
    <div data-bind="if: ($index() === ($parent.Items().length - 1))">I'm the last element</div>
</div>

Le secret avec le remplacement de $parent.data() par le nom du tableau observable que vous utilisez Dans mon cas, il s'appelait Items, donc j'ai remplacé le $parent.data() par $parent.Items()

NOTE _ ​​cette solution fonctionne dans tous les cas, que vous utilisiez ou non l'option as
mais dans le premier cas, cela résout quelque chose que la réponse la plus votée n'a pas résolu
}

3
Hakam Fostok

Essayez ce qui suit:

  1. Utilisez $root au lieu de $parent
  2. Préparez le dernier élément à l'avance et transmettez-le à votre modèle en tant que paramètre supplémentaire. 
  3. Encapsuler le dernier élément à observable. 
0
Madman