2011-12-27 3 views
59

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

Tengo un modelo de vista padre con una colección de niños, y el modelo de vista padre tiene un selectedChild observable. Teniendo en cuenta que, puedo utilizar expresiones de enlace de datos para agregar una clase CSS a cualquier niño está actualmente seleccionado: "estoy seleccionado"

<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 ViewModels se van a poner más complejo, y me gustaría para poder hacer algo más que simplemente agregar una única clase de CSS a un solo elemento. Realmente quiero hacer una propiedad computada isSelected en el modelo de vista hijo, entonces puedo agregar otras propiedades calculadas que dependen de él.

He intentado sólo escribir código JavaScript que se refiere a $data y $root, en la remota posibilidad de que nocaut podría definir estas variables y de alguna manera hacer que sean de alcance cuando llama a mi función computed evaluador:

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

Pero no tuve suerte: dentro de mi evaluador function, ambos $data y $root son undefined.

También intenté usar ko.contextFor dentro de mi evaluador, ya que da acceso a $data y $root. Desafortunadamente, 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 anterior se une a un observable computarizada - después de todo, that's what knockout already does:

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

Pero, ¿cómo hago para hacer frente a los $data y $root pseudovariables cuando estoy escribiendo mi propia observables computarizada?

Respuesta

76

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 observable calculado que indique si este elemento es igual al elemento seleccionado en el modelo de vista principal, entonces deberá encontrar una forma de hacer que el padre esté disponible para el niño.

Una opción es pasar la matriz en 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 así 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 importa es el estado seleccionado, entonces se puede ajustar a pasar una referencia a la selectedItem observable al constructor niño 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.

+0

¡gracias por su segundo ejemplo! – vittore

+0

¿cómo llamarías a una función en la raíz, así como haciendo clic: $ root.selectedItem, dentro del mismo enlace? – FutuToad

+0

por ejemplo, esto no funciona: haga clic en: function() {$ parent.openAlertDialogueEdit ($ data) //} $ de datos parece ser una copia, no la referencia real – FutuToad

-7

Use $context en lugar de $parent cuando esté dentro de un enlace foreach.

1

En mi experiencia, el enfoque en la respuesta de @RP Niemeyer está bien si Item s en vivo durante la duración de la aplicación. Pero si no, puede ocasionar fugas de memoria, ya que Item's observable computable establece una dependencia inversa del 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.

Usted podría asegurarse de disponer() de la calculada, tal vez en un método de limpieza() en Item que se llama cuando el elemento se va, pero hay que recordar hacer que cada vez que la eliminación de Item s.

En su lugar, ¿por qué no hacer Item un poco menos inteligente y tener ViewModel decirlo cuando se selecciona? Simplemente haga Item 's isSelected() una vieja observable y luego en ViewModel suscribirse a selectedItem y una actualización periódica dentro de esa suscripción.

O utilice @RP Niemeyer's pub/sub solution. (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.

Consulte la respuesta a my recent question sobre este mismo tema para obtener más detalles.

Cuestiones relacionadas