59 votes

Comment puis-je utiliser le knock-out de $parent/$root pseudovariables de l'intérieur .calculer() observables?

À l'intérieur d'un knockout.js expression de liaison, je peux utiliser l' $data, $parent, et $root pseudovariables. Comment puis-je obtenir l'équivalent de ces pseudovariables quand je suis à l'aide d'un ko.computed observables déclaré en JavaScript?

J'ai un parent viewmodel avec une collection de l'enfant et le parent viewmodel a un selectedChild observables. Étant donné que, je peux utiliser des expressions de liaison de données pour ajouter une classe CSS à celle de l'enfant qui est actuellement sélectionné:

<ul data-bind="foreach: children">
    <li data-bind="text: name,
                   css: {selected: $data === $root.selectedChild()},
                   click: $root.selectChild"></li>
</ul>
<script>
vm = {
    selectedChild: ko.observable(),
    children: [{name: 'Bob'}, {name: 'Ned'}],
    selectChild: function(child) { vm.selectedChild(child); }
};
ko.applyBindings(vm);
</script>

Mais mon viewmodel sont en cours pour obtenir plus complexe, et j'aimerais "suis-je choisi?" pour être en mesure de faire plus que simplement l'ajout d'une seule classe CSS à un élément unique. J'ai vraiment envie de faire un isSelected propriété calculée sur l'enfant viewmodel, donc, je peux alors ajouter d'autres propriétés calculées qui en dépendent.

J'ai essayé d'écrire du JavaScript qui fait référence à l' $data et $root, sur la chance que knock-out pourrait définir ces variables et en quelque sorte de leur portée quand elle appelle mon computed évaluateur de fonction:

{
  name: 'Bob',
  isSelected: ko.computed(function(){ return $data === $root.selectedChild(); })
}

Mais pas de chance: à l'intérieur de mon évaluateur function, les deux $data et $root sont undefined.

J'ai aussi essayé d'utiliser ko.contextFor à l'intérieur de mon évaluateur, car il donne accès à l' $data et $root. Malheureusement, à l'intérieur de mon évaluateur de fonction, contextFor également toujours les retours undefined. (Je n'ai pas de grands espoirs pour cette stratégie de toute manière, il n'est pas évident de knock-out serait en mesure de suivre les dépendances si je devais aller derrière son dos comme ça.)

J'ai toujours pu définir manuellement une propriété sur chaque enfant viewmodel qui fait référence à la société mère viewmodel. Mais je sais que knock-out a la capacité de le faire pour moi, et je voudrais explorer au moins savoir si je peut utiliser ses mécanismes avant de m'en aller écrire mon propre.

Il me semble qu'il devrait être possible de traduire le ci-dessus expression de liaison à un calculées observables -- après tout, c'est ce que knock-out déjà fait:

L'autre astuce est que déclarative liaisons sont simplement mises en œuvre calculé observables.

Mais comment dois-je traiter avec l' $data et $root pseudovariables quand j'écris mes propres calculées observables?

77voto

RP Niemeyer Points 81663

Le pseudovariables ne sont disponibles que dans le cadre de la liaison de données. Le modèle de vue lui-même, idéalement, ne devraient pas connaître ou de dépendances sur le point de vue qui est de l'afficher.

Ainsi, lors de l'ajout d'calculée observables dans le modèle de vue, vous n'avez aucune connaissance de la façon dont il va être lié (comme ce qui se passe à $racine). Un modèle de vue ou une partie d'un modèle de vue pourrait même être lié séparément à plusieurs zones de la page, à différents niveaux, de sorte que le pseudo-variables serait différent en fonction de l'élément que vous démarrez avec.

Cela dépend de ce que vous essayez d'accomplir, mais si vous voulez que votre enfant d'avoir un isSelected calculé observables qui indique si l'élément est le même que l'élément sélectionné dans la vue parent modèle, alors vous aurez besoin de trouver un moyen de rendre le parent disponible pour l'enfant.

Une option est de passer le parent dans la fonction constructeur de votre enfant. Vous n'avez même pas besoin d'ajouter le pointeur de la société mère à la propriété de l'enfant et peut simplement utiliser dans votre calculée observables directement.

Quelque chose comme:

var Item = function(name, parent) {
   this.name = ko.observable(name);  
   this.isSelected = ko.computed(function() {
       return this === parent.selectedItem();        
   }, this);
};

var ViewModel = function() {
   this.selectedItem = ko.observable();
   this.items = ko.observableArray([
       new Item("one", this),
       new Item("two", this),
       new Item("three", this)
       ]);
};

Exemple ici: http://jsfiddle.net/rniemeyer/BuH7N/

Si tout ce qui vous intéresse est le statut sélectionné, vous pouvez modifier à passer une référence à l' selectedItem observables à l'enfant du constructeur comme: http://jsfiddle.net/rniemeyer/R5MtC/

Si votre parent modèle de vue est stockée dans une variable globale, alors vous pourriez envisager de ne pas la transmettre à l'enfant et de l'utiliser directement comme: http://jsfiddle.net/rniemeyer/3drUL/. Je préfère passer la référence de l'enfant.

1voto

David M. Brown Points 1084

Dans mon expérience, l'approche de l' @RP Niemeyer réponse est bien si Items live pour la durée de l'application. Mais si pas, il peut conduire à des fuites de mémoire, car Item's calculée observables met en place une inversion de dépendance de l' ViewModel. Encore une fois, c'est ok si vous n'avez jamais à se débarrasser de tout Item objets. Mais si vous essayez de se débarrasser de l' Items ils ne seront pas les ordures collectées, car knock-out aura toujours qui inverse la dépendance de référence.

Vous pourriez assurez-vous de dispose() de l'calculée, peut-être dans un cleanup() la méthode d' Item qui est appelée lorsque l'élément disparaît, mais vous devez vous rappeler de faire que chaque fois que la suppression Items.

Au lieu de cela, pourquoi ne pas faire Item un peu moins intelligents et qu'ils ont ViewModel dire lorsqu'il est sélectionné? Juste faire de l' Items' isSelected() régulièrement vieux observables et puis, en ViewModel abonnez-vous à selectedItem et de mise à jour à l'intérieur de cet abonnement.

Ou, utilisez @RP Niemeyer pub/sub solution. (Pour être juste, cette solution est née à la suite de sa réponse ici.) Vous aurez toujours besoin de nettoyer, cependant, car elle crée des dépendances inverses, trop. Mais au moins il y a de moins en moins de couplage.

Voir la réponse à ma récente question sur ce même sujet pour plus de détails.

-2voto

Kyle Points 787

Vous pouvez également définir une propriété observable dans votre ViewModel et puis dans le code HTML de liaison définir à l'aide de l' $parent contexte

Dans le Js

this.Parent = ko.observable();

Dans la page HTML

    <!-- ko if: Parent($parent) --><!-- /ko -->

-7voto

Peter Points 1

Utiliser $context au lieu de $parent quand, dans un foreach de liaison.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X