J'ai un observableArray simple qui contient beaucoup de modèles utilisateur. Dans le balisage, il y a un modèle avec une boucle foreach qui boucle les utilisateurs et les affiche dans un tableau simple. En outre, je style la table avec une barre de défilement personnalisée et quelques autres javascript. Alors maintenant, je dois savoir quand la boucle foreach est terminée et que tous les modèles sont ajoutés au DOM.
Le problème avec le rappel afterRender est qu'il est appelé chaque fois que quelque chose est ajouté, mais j'ai besoin d'un type de rappel qui ne se déclenche qu'une seule fois.
Votre meilleur pari est d'utiliser une liaison personnalisée. Vous pouvez placer votre liaison personnalisée après foreach
dans la liste des liaisons de votre data-bind
ou exécuter votre code dans une setTimeout
afin de permettre à foreach
de générer le contenu avant que votre code ne soit exécuté.
Voici un exemple montrant le code en cours d'exécution une seule fois et chaque fois que votre observableArray est mis à jour: http://jsfiddle.net/rniemeyer/Ampng/
HTML:
<table data-bind="foreach: items, updateTableOnce: true">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
</tr>
</table>
<hr/>
<table data-bind="foreach: items, updateTableEachTimeItChanges: true">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
</tr>
</table>
<button data-bind="click: addItem">Add Item</button>
JS:
var getRandomColor = function() {
return 'rgb(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ')';
};
ko.bindingHandlers.updateTableOnce = {
init: function(element) {
$(element).css("color", getRandomColor());
}
};
//this binding currently takes advantage of the fact that all bindings in a data-bind will be triggered together, so it can use the "foreach" dependencies
ko.bindingHandlers.updateTableEachTimeItChanges = {
update: function(element) {
$(element).css("color", getRandomColor());
}
};
var viewModel = {
items: ko.observableArray([
{ id: 1, name: "one" },
{ id: 1, name: "one" },
{ id: 1, name: "one" }
]),
addItem: function() {
this.items.Push({ id: 0, name: "new" });
}
};
ko.applyBindings(viewModel);
Je suis venu avec une astuce élégante. Immédiatement après votre bloc template/foreach, ajoutez ce code:
<!--ko foreach: { data: ['1'], afterRender: YourAfterRenderFunction } -->
<!--/ko-->
Un moyen simple et rapide consiste à comparer, dans votre gestionnaire afterRender, l'élément actuel au dernier élément de votre liste. Si cela correspond, il s'agit de la dernière fois que afterRender est exécuté.
La réponse de Jaffa contient une erreur, j'ai donc décidé de créer une nouvelle réponse au lieu de commenter. Il n’est pas possible d’utiliser simultanément un modèle et un modèle. Il suffit donc de déplacer votre modèle vers la balise data
du modèle.
Html
<div data-bind="template: {data: myModel, afterRender: onAfterRenderFunc }" >
<div data-bind="foreach: observableArrayItems">
...
</div>
</div>
Javascript
var myModel = {
observableArrayItems : ko.observableArray(),
onAfterRenderFunc: function(){
console.log('onAfterRenderFunc')
}
}
ko.applyBinding(myModel);
Je ne sais pas si la réponse acceptée fonctionnera dans knockout-3.x (car les liaisons de données ne sont plus exécutées dans l'ordre dans lequel vous les déclarez).
Voici une autre option, il ne sera déclenché qu’une seule fois.
ko.bindingHandlers.mybinding {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var foreach = allBindings().foreach,
subscription = foreach.subscribe(function (newValue) {
// do something here
subscription.dispose(); // dispose it if you want this to happen exactly once and never again.
});
}
}
Pour savoir quand le modèle foreach a terminé le rendu, vous pouvez créer une liaison "fictive", transmettre l'index de l'élément actuellement rendu à votre gestionnaire et vérifier s'il correspond à la longueur du tableau.
HTML:
<ul data-bind="foreach: list">
<li>
\\ <!-- Your foreach template here -->
<div data-bind="if: $root.listLoaded($index())"></div>
</li>
</ul>
ViewModel - Gestionnaire:
this.listLoaded = function(index){
if(index === list().length - 1)
console.log("this is the last item");
}
Tenez l'indicateur supplémentaire si vous souhaitez ajouter d'autres éléments à la liste.
Que diriez-vous d'utiliser un debouncer?
var afterRender = _.debounce(function(){
// code that should only fire 50ms after all the calls to it have stopped
}, 50, { leading: false, trailing: true})
Juste au dessus de ma tête, vous pouvez soit:
Exemple:
<div>
<div data-bind="with: parentItem(), template: { afterRender: myRenderFunc }" >
<div data-bind="foreach: observableArrayItems">
...
</div>
</div>
</div>
Pas testé cela alors devinez ...