2012-01-22 15 views
19

Diciamo che abbiamo dati come segueprogressive enhancement con KnockoutJS

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

Possiamo facilmente rappresentare questo come un modello di vista legato ad un modello di eliminazione diretta come segue:

<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 domanda è: come possiamo ottenere lo stesso risultato usando il miglioramento progressivo? Ovvero avendo inizialmente il rendering del template sul lato server e quindi vincendo il modello ad eliminazione diretta e il modello di visualizzazione a quel rendering.

Un modello lato server semplice sarebbe simile a questa:

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

ho esplorato alcune possibilità differenti:

  • Uno è creare un modello knockout e un modello lato server, e generare dinamicamente il modello di visualizzazione Knockout analizzando il DOM per il modello lato server. In questo modo, solo il modello Knockout sarà visibile quando JavaScript è abilitato e solo il modello lato server sarà visibile se JavaScript è disabilitato. Potrebbero essere abbinati in modo da renderli identici.

  • Un altro approccio consiste nell'applicare i collegamenti per ciascun elemento nell'array di facet separatamente all'elemento DOM esistente per tale facet. Tuttavia, questo è ancora solo un livello profondo e non funziona per gli elementi nidificati.

Nessuno di questi approcci sembra piuttosto pulito. Un'altra soluzione potrebbe essere quella di scrivere un'associazione personalizzata che gestisca l'intero rendering e riutilizza gli elementi esistenti, se possibile.

Altre idee?

+0

ho rinunciato a progressive enhancement con Knockout. Al di là di qualsiasi cosa * super-banale * non è proprio pratico mantenere sincronizzato il comportamento non KO/KO. Questo è un problema con il PE JavaScript standard, ma il ricco modello di binding di KO lo rende * una divergenza estrema * negli approcci . (Forse c'è un progetto "Server-side KO"? Sarebbe .. interessante per non dire altro.) – user2864740

risposta

4

Ho esplorato diversi approcci qui, compreso quello di generare un modello anonima dal primo elemento, come descritto qui:

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

o la creazione di legami separati per ciascun elemento della matrice attraverso un legame personalizzato come

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

che applica un legame specifico a ciascun elemento e quindi sul primo aggiornamento sostituisce il contenuto con un ordinario vincolo foreach. Questo approccio significa che devi ancora avere due modelli. Entrambi implicano l'inizializzazione dello stato tramite un JSON reso dal server.

L'approccio corrente che ho scelto di utilizzare (ancora soggetto a modifiche, però) è di mettere tutti i modelli Knockout all'interno dei tag script in modo che non vengano mai visualizzati nei browser NoJS.I modelli NoJS sono resi come contenuti di un div sul lato server. Non appena viene eseguito il rendering del modello Knockout, il contenuto del div sarà sostituito dal modello Knockout nei tag dello script. Puoi modellarli in modi identici/simili per rendere la transizione senza interruzioni e, se ciò non è possibile, nascondere il modello noJS tramite CSS.

Alla fine, sono giunto alla conclusione che Knockout.js e il miglioramento progressivo non funzionano molto bene insieme, uno dovrebbe scegliere l'altro, ovvero costruire alcune parti dell'applicazione che richiedono un miglioramento progressivo utilizzando più tradizionale metodi come la manipolazione diretta di jQuery DOM.

+0

+1 Sono arrivato alla stessa conclusione - senza supporto completo lato server per KO (un nuovo progetto, chiunque?), è anche troppo divergente di un approccio da un modello tradizionale HTML/postback per rimanere praticamente sincronizzato. – user2864740

3

Basta aggiungere i vari attributi data- nei modelli lato server. Non fanno male se JavaScript è disabilitato quindi averli non è affatto un problema.

+3

Che funziona solo se hai una struttura dati piatta, come gestiresti array e array annidati? –

+0

Sì, anche aggiungendo e rimuovendo le righe dai tavoli, quel genere di cose. –

3

Ho paura che non ci sia un modo pulito per farlo. Personalmente eseguo il rendering della pagina nel backend e poi passo gli stessi dati di contesto al frontend (serializzato come JSON) e configuro Knockout usandolo. Ciò significa che c'è qualche duplicazione. Forse passare il backend a node.js semplificerebbe le cose qui.

+1

Sono quasi arrivato alla stessa conclusione, sfortunatamente. Forse sarà migliorato in una versione futura di Knockout (cioè attraverso un modo per legarsi agli elementi dom esistenti). –

+0

Tomasz, curioso di sapere come si esegue il rendering lato server - stai usando il nodo? rinoceronte? qualcos'altro? Nel complesso, è curioso sapere se si è riusciti a utilizzare gli stessi modelli lato server come sul client o se è necessario mantenere due serie di modelli. –

+0

@Karl: il back-end era Django, quindi i modelli assomigliavano a questo:

{{ my_data }}

0

here, ho suggerito un collegamento di modello che utilizza l'HTML generato dal server. L'utilizzo è simile al binding Template originale, ad eccezione dei pochi dati-attributi che verranno utilizzati dal binding.

+0

Se si utilizza * qualsiasi * KO, perché non utilizzare tutto KO? Avendo KO lavorare su una parte, praticamente si rompe la maggior parte delle situazioni di PE (non c'è niente su cui lavorare, ma KO abbastanza banale può essere rimosso banalmente e mantenere funzionalità). – user2864740

0

Il miglioramento progressivo è molto più semplice con i dati non in loop come i moduli (come ho scritto qui: http://www.tysoncadenhead.com/blog/using-knockout-for-progressive-enhancement). Personalmente, penso che il looping su più elementi nel DOM e il re-rendering degli stessi sembrerebbe un'implementazione difficile, ma è difficile pensare a qualcosa di meglio.

+0

Ecco un esempio di come è possibile eseguire il loop su un gruppo di elementi sul server e quindi re-disegnarli in un ciclo foreach di Knockout: http://www.tysoncadenhead.com/blog/using-knockout-for-progressive- valorizzazione-on-liste –

1

Ecco il mio esempio, questo esempio di base utilizza un'associazione personalizzata per caricare i valori lato server nel modello di visualizzazione.

Progressive Enhancement with KnockoutJS Infinite Scroll

screenshot

Il progressive enhancement vincolante

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