2012-01-22 14 views
19

Supongamos que tenemos datos de la siguiente maneraprogresiva con KnockoutJS

var data = { 
    facets: [{ 
    name : "some name", 
    values: [{ 
     value: "some value" 
    }] 
    }] 
}; 

Podemos representar fácilmente esto como un modelo de vista unida a una plantilla de nocaut de la siguiente manera:

<ul data-bind="foreach: facets">  
    <li>  
    <span data-bind="text: name"></span> 
    <ul data-bind="foreach: values">    
     <li data-bind="text: value"></li>  
    </ul> 
    </li> 
</ul> 

La pregunta es, ¿cómo podemos lograr el mismo resultado cuando usamos mejora progresiva? Esto es haciendo que la plantilla se represente inicialmente en el servidor y luego vincule la plantilla knockout y el modelo de vista a esa representación.

Una plantilla lado del servidor sencilla sería algo como esto:

<ul>  
    <li>  
    <span>some name</span> 
    <ul>    
     <li>some value</li>  
    </ul> 
    </li> 
</ul> 

he explorado algunas posibilidades diferentes:

  • Uno es crear una plantilla nocaut y una plantilla del lado del servidor, y genere el modelo de vista Knockout dinámicamente mediante el análisis del DOM para la plantilla del lado del servidor. De esta forma, solo la plantilla Knockout sería visible cuando JavaScript esté habilitado, y solo la plantilla del lado del servidor estaría visible si JavaScript está deshabilitado. Podrían ser diseñados de una manera para que se vean idénticos.

  • Otro enfoque es aplicar enlaces para cada elemento en la matriz de facetas por separado al elemento DOM existente para esa faceta. Sin embargo, este solo tiene un nivel de profundidad y no funciona para los elementos anidados.

Ninguno de estos enfoques parece bastante limpio. Otra solución podría ser escribir un enlace personalizado que maneje todo el renderizado y reutilice elementos existentes si es posible.

¿Alguna otra idea?

+0

Yo he renunciado a la mejora progresiva con el golpe de gracia. Más allá de cualquier cosa * súper-trivial * no es práctico mantener el comportamiento no KO/KO sincronizado ... este es un problema incluso con JavaScript PE estándar, pero el rico modelo vinculante de KO lo convierte en una * extrema divergencia * en los enfoques . (¿Tal vez hay un proyecto "KO del lado del servidor"? Eso sería ... por lo menos interesante). – user2864740

Respuesta

4

I exploraron varios enfoques aquí, incluyendo la generación de una plantilla de anónimo desde el primer elemento, como se describe aquí:

http://groups.google.com/group/knockoutjs/browse_thread/thread/3896a640583763d7

o la creación de enlaces separados para cada elemento de la matriz a través de una unión de encargo, tal como

ko.bindingHandlers.prerenderedArray = { 
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) { 
     var binding = valueAccessor();    
     binding.firstTime = true; 

     $(element).children().each(function(index, node) {     
      var value = ko.utils.unwrapObservable(binding.value)[index];       
      ko.applyBindings(value, node); 
     }); 

     return { 'controlsDescendantBindings': true };    
    }, 
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {    
     var binding = valueAccessor(); 
     if (binding.firstTime) { 
      binding.firstTime = false; 
      return; 
     }    

     $(element).children().remove(); 
     ko.applyBindingsToNode(element, { template: { name: binding.template, foreach: binding.value }}, viewModel) 
    } 
};  

que aplica un enlace específico a cada elemento y luego en la primera actualización reemplaza los contenidos con un enlace foreach ordinario. Este enfoque todavía significa que aún necesita tener dos plantillas. Ambos también implican inicializar el estado a través de un JSON procesado por el servidor.

El enfoque actual que opté por usar (aunque todavía está sujeto a cambios) es colocar todas las plantillas Knockout dentro de las etiquetas de script para que nunca se procesen en los navegadores NoJS.Las plantillas NoJS se representan como contenido de un div en el lado del servidor. Tan pronto como se procesa la plantilla Knockout, el contenido del div se reemplazará por la plantilla Knockout en las etiquetas del script. Puede diseñarlos de forma idéntica/similar para que la transición sea perfecta, y si esto no es posible, oculte la plantilla noJS a través de CSS.

Al final, llegué a la conclusión de que Knockout.js y la mejora progresiva en realidad no funcionan muy bien juntos, uno debe elegir cualquiera de los otros, es decir, construir algunas partes de la aplicación que requieren una mejora progresiva utilizando métodos tales como la manipulación directa de jQuery DOM.

+0

+1 He llegado a la misma conclusión, sin soporte completo del lado del servidor para KO (¿un nuevo proyecto, alguien?), simplemente es demasiado divergente de un enfoque de un modelo tradicional de HTML/postback para mantenerse sincronizado prácticamente. – user2864740

3

Simplemente agregue los diversos atributos data- en las plantillas del lado del servidor. No perjudican si JavaScript está desactivado, por lo que tenerlos no es un problema en absoluto.

+3

Eso funciona solo si tienes una estructura de datos plana, ¿cómo manejarías las matrices y las matrices anidadas? –

+0

Sí, también agregando y eliminando líneas de las tablas, ese tipo de cosas. –

3

Me temo que no hay una manera clara de hacerlo. Personalmente renderizo la página en el back-end y luego paso los mismos datos contextuales a la interfaz (serializados como JSON) y configuré Knockout usándolo. Esto significa que hay algo de duplicación. Quizás cambiar el backend a node.js simplificaría las cosas aquí.

+1

Casi llegué a la misma conclusión, desafortunadamente. Tal vez se mejorará en una versión futura de Knockout (es decir, de alguna manera para enlazar a elementos dom existentes). –

+0

Tomasz, curioso cómo renderiza el lado del servidor - ¿está usando nodo? ¿rinoceronte? ¿algo más? En general, es curioso si ha logrado usar las mismas plantillas del lado del servidor que en el cliente, o si debe mantener dos conjuntos de plantillas. –

+0

@Karl: El backend era Django, por lo que las plantillas se veían así:

{{ my_data }}

0

here, sugerí un enlace de plantilla que utiliza el HTML generado del servidor. Su uso es similar al enlace de plantilla original, excepto por los pocos atributos de datos que usará el enlace.

+0

Si usa * cualquier * KO, ¿por qué no usar todos los KO? Hacer que KO trabaje solo en una parte casi rompe la mayoría de las situaciones de PE (no hay nada en lo que trabaje que sea lo suficientemente trivial como para eliminar trivialmente KO y mantener la funcionalidad). – user2864740

0

La mejora progresiva es mucho más fácil con datos sin bucles como formularios (como escribí aquí: http://www.tysoncadenhead.com/blog/using-knockout-for-progressive-enhancement). Personalmente, creo que al pasar por varios ítems en el DOM y volver a representarlos parece que sería una implementación difícil, pero es difícil pensar en algo mejor.

+0

Aquí hay un ejemplo de cómo se puede recorrer un conjunto de elementos en el servidor y luego volver a dibujarlos en un bucle foreach Knockout: http://www.tysoncadenhead.com/blog/using-knockout-for-progressive- mejora en las listas –

1

Aquí está mi opinión, este ejemplo básico usa un enlace personalizado para cargar los valores del lado del servidor en el modelo de vista.

Progressive Enhancement with KnockoutJS Infinite Scroll

screenshot

Mejora progresiva unión

ko.bindingHandlers.PE = { 
    init: function(element, valueAccessor, allBindings) { 
     var bindings = allBindings(); 
     if (typeof bindings.text === 'function') { 
      bindings.text($(element).text()); 
     } 
    } 
};