javascript knockout.js

javascript - ¿Cómo puedo usar las pseudovariables $ parent/$ root de knockout desde dentro de un elemento observable de.computed()?



knockout.js (3)

En mi experiencia, el enfoque en la respuesta de @RP Niemeyer está bien si los Item viven en la duración de la aplicación. Pero si no, puede provocar fugas de memoria, ya que el Item observable calculado de Item establece una dependencia inversa de ViewModel . De nuevo, está bien si nunca se deshace de ningún objeto Item . Pero si tratas de deshacerte de Item s no obtendrán basura recolectada porque knockout seguirá teniendo esa referencia de dependencia inversa.

Puede asegurarse de deshacerse () del método calculado, quizás en un método de limpieza () en el Item que se llama cuando el artículo se va, pero debe recordar hacerlo cada vez que elimine los Item .

En cambio, ¿por qué no hacer que el Item un poco menos inteligente y que ViewModel se lo ViewModel cuando se selecciona? Simplemente haga que el Item isSelected() un observable antiguo normal y luego en ViewModel suscríbase al selectedItem y actualice dentro de esa suscripción.

O utilice la solución de pub / sub de @RP Niemeyer. (Para ser justos, esta solución surgió después de su respuesta aquí). Sin embargo, todavía tendrá que limpiar, porque crea dependencias inversas también. Pero al menos hay menos acoplamiento.

Vea la respuesta a mi pregunta reciente sobre este mismo tema para más detalles.

Dentro de una expresión de enlace de knockout.js , puedo usar las pseudovariables $data , $parent y $root . ¿Cómo puedo obtener el equivalente de esas pseudovariables cuando estoy usando un observable ko.computed declarado en JavaScript?

Tengo un modelo de vista padre con una colección de hijos, y el modelo de vista padre tiene un elemento visible para niños. Dado que, puedo usar expresiones de enlace de datos para agregar una clase de CSS a cualquier niño que esté seleccionado actualmente:

<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>

Pero mis modelos de vista se volverán más complejos, y me gustaría "¿soy seleccionado?" para poder hacer algo más que simplemente agregar una única clase de CSS a un solo elemento. Realmente quiero hacer una propiedad calculada de isSelected en el modelo de vista hijo, así puedo agregar otras propiedades calculadas que dependen de él.

Intenté simplemente escribir JavaScript que se refiera a $data y $root , en caso de que el knockout pudiera definir esas variables y de alguna manera tenerlas en alcance cuando llame a mi función de evaluador computed :

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

Pero no tuve suerte: dentro de mi function evaluador, tanto $data como $root undefined están undefined .

También intenté usar ko.contextFor dentro de mi evaluador, ya que da acceso a $data y $root . Lamentablemente, dentro de mi función de evaluador, contextFor también siempre devuelve undefined . (De todos modos, no tenía grandes esperanzas para esta estrategia, no está claro qué tan bien podría ser capaz de rastrear las dependencias si tuviera que ir detrás de su espalda así).

Siempre pude establecer manualmente una propiedad en cada modelo de vista hijo que hace referencia al modelo de vista padre. Pero sé que el nocaut tiene la capacidad de hacer esto por mí, y al menos me gustaría explorar si puedo usar sus mecanismos antes de escribir el mío.

Parece que debería ser posible traducir la expresión de enlace anterior a una observable calculada, después de todo, eso es lo que hace el knockout :

El otro truco es que las consolidaciones declarativas simplemente se implementan como observables computados.

¿Pero cómo trato de las pseudovariables $data y $root cuando estoy escribiendo mi propio observable calculado?


Las pseudovariables solo están disponibles en el contexto del enlace de datos. El modelo de vista idealmente no debería conocer ni tener ninguna dependencia de la vista que lo muestra.

Por lo tanto, al agregar observables calculados en el modelo de vista, no tiene conocimiento de cómo se vinculará (como lo que será $ root). Un modelo de vista o parte de un modelo de vista podría incluso vincularse por separado a múltiples áreas de la página en diferentes niveles, por lo que las pseudovariables serían diferentes según el elemento con el que se inicie.

Depende de lo que intente lograr, pero si desea que su hijo tenga un isSelected computed observable que indique si este elemento es igual al elemento seleccionado en el modelo de vista principal, entonces deberá encontrar una manera de hacerlo. el padre disponible para el niño.

Una opción es pasar al padre a la función constructora de su hijo. Ni siquiera necesita agregar el puntero al padre como una propiedad del niño y solo puede usarlo en su observable calculado directamente.

Algo como:

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) ]); };

Muestra aquí: http://jsfiddle.net/rniemeyer/BuH7N/

Si todo lo que le interesa es el estado seleccionado, puede ajustarlo para pasar una referencia al elemento selectedItem observable al constructor hijo como: http://jsfiddle.net/rniemeyer/R5MtC/

Si su modelo de vista padre está almacenado en una variable global, entonces podría considerar no pasarlo al niño y usarlo directamente como: http://jsfiddle.net/rniemeyer/3drUL/ . Sin embargo, prefiero pasarle la referencia al niño.


Use $context lugar de $parent cuando se encuentre dentro de un enlace foreach.