Je travaille avec knockout.js pour créer des listes dynamiques et j'essaie de comprendre comment je peux obtenir l'objet DOM associé à un objet dans mon tableau observable. Plus précisément, je veux obtenir le jQuery pour une ligne.
Exemple:
<ul data-bind="foreach: Item">
<li data-bind="events: {click: getDomObject}, text: 'text: ' + text">
</li>
</ul>
dans la fonction getDomObject
, je voudrais pouvoir obtenir le <li></li>
Objet DOM pour que je puisse faire une manipulation jQuery avec lui.
J'ai pensé à ajouter un membre id
au Item ViewModel, puis ajouter l'identifiant comme identifiant html de l'élément de campagne, puis sélectionner en fonction de cela, mais je pense qu'il devrait y avoir un moyen plus simple.
Quelle est la bonne façon de référencer le HTML dynamique généré par knockout.js?
Les gestionnaires d'événements comme click obtiennent deux arguments. C'est
l'élément auquel cet événement appartient - comme l'entrée d'un tableau observable que vous restituez avec la liaison foreach ("Item" dans votre cas).
Et, un objet d'événement, qui vous fournit plus d'informations sur l'événement réel. Cet objet contient l'élément DOM sur lequel on a cliqué (clé "cible"):
getDomObject = function(item, event) {
var $this = $(event.target);
// ...
}
Juste une note: ne mélangez pas les manipulations de knockout et natives de jQuery DOM - si vous pouvez obtenir le même résultat avec des liaisons de knockout intelligentes, je recommanderais d'y aller.
Et voici une démo simple: http://jsfiddle.net/KLK9Z/213/
var Item = function(color) {
this.color = String(color);
this.setTextColor = function(item, event) {
$(event.target).css('background', color);
};
};
ko.applyBindings(new function() {
this.Items = ko.observableArray([
new Item('red'),
new Item('blue'),
new Item('green')
]);
}());
li {
padding: 2px 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.0.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<ul data-bind="foreach: Items">
<li>
<button data-bind="click: setTextColor, text: 'Color: ' + color"></button>
</li>
</ul>
La solution $ (event.target) est bonne si elle est liée à un événement déjà survenu dans lequel l'élément DOM de l'élément est ciblé. Mais parfois, vous n'avez pas l'élément ciblé car il n'y a pas d'événement (par exemple - vous souhaitez faire défiler une liste vers un élément qui n'a pas été signalé par l'utilisateur).
Dans ce cas, vous pouvez attribuer à l'attribut id de l'élément DOM de l'élément une valeur unique qui contient l'ID de l'élément:
<li data-bind="attr: {id: 'item_' + id}">
puis getDomObject () ressemble à:
getDomObject = function(item) { return $("#item_" + item.id); }
Pour ajouter encore une 3ème option, également pour les cas où vous n'avez pas d'événement avec lequel travailler (si vous avez un événement, la réponse acceptée est meilleure/optimisée).
Créez une liaison personnalisée telle que:
ko.bindingHandlers.scrollTo = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
var scrollParent = $(element).closest("div");
var newTop = $(element).position().top + scrollParent.scrollTop();
scrollParent.scrollTop(newTop);
}
}
};
l'utilisation est la suivante:
<li data-bind="scrollTo: $parent.scrollTo() && $parent.scrollTo().id == id">
Dans le cas ci-dessus, $ parent est mon modèle d'affichage. J'ai un objet observable qui contient un identifiant unique. Chaque fois que je mets cet objet scrollTo (), la liste défile jusqu'à cet élément.
Notez que mon code suppose que le DIV parent du LI a la barre de défilement (débordement: auto/scroll). Vous pouvez vous adapter à vos besoins, éventuellement utiliser une classe sur le parent et l'utiliser pour votre sélecteur jQuery, ou pour être très flexible, vous pouvez passer le sélecteur via vos options de liaison de données ... pour moi, cela suffisait, car J'utilise toujours des divs pour mes sections déroulantes.
J'avais un problème similaire. Je viens avec une solution ressemblant à l'utilisation Backbone.js des références el et $ el.
dans votre ViewModel:
var myViewModel = function(){
var self = this;
//html element
self.el = ko.observable();
//jquery wrapped version
self.$el = ko.observable();
}
en html (par exemple élément list):
<!-- left side is the name of the handler, right side is name of the observable -->
<li class="myclass" data-bind="el: el, $el: $el"></li>
dans bindingHandlers (montrant tous les arguments possibles pour init):
ko.bindingHandlers.el = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = valueAccessor();
//assign value to observable (we specified in html)
value(element);
}
};
ko.bindingHandlers.$el = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = valueAccessor();
//here we first create a jQuery object by using $(myelem)
//before updating observable value
value($(element).first());
}
};
Par exemple, vous pouvez utiliser $ el comme:
var myViewModel = function(){
var self = this;
//plain DOM element reference
self.el = ko.observable();
//jquery object reference
self.$el = ko.observable();
self.myFunction = function() {
console.log(self.$el().html());
self.$el().addClass("myCssClass");
}
}
J'espère que cela t'aides!
Ma solution (valable pour la reliure "value")
ko.bindingHandlers.value.preprocess = function(val, name, cb) {
/* every time I set a data-bind="value: xxxx" with an
* observable xxxx add also a data-bind="domElement: xxxx" */
cb('domElement', val );
return val;
}
ko.bindingHandlers.domElement = {
/* For each data-bind="domElement: xxxx" add an extension "element" */
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
valueAccessor().extend({element: element });
}
};
ko.extenders.element = function (target, element) {
/* element extension add el and $el to observable xxxx */
target.el = element;
target.$el = $(element);
}
Vous avez maintenant yourobservable. $ El et yourobservable.el qui se lient à l'élément jquery et DOM.