2013-07-30 13 views
6

Sto cercando di capire dove si trova il posto migliore per eseguire un'operazione di carico di lunga durata utilizzando Durandal.Operazioni di carico a lungo termine in durezza

Da quello che posso dire, la raccomandazione generale per il caricamento dei dati è nel metodo activate del ViewModel, che è quello che faccio di solito - qualcosa come:

viewModel.activate = function() { 
    var loadPromise = myService.loadData(); 

    return $.when(loadPromise).then(function (loadedData) { 
     viewModel.data(data); 
    }); 
}; 

So che se non restituire il promettono qui, quindi di solito ci sono problemi con i binding - come this question and answer indicates.

Tuttavia, l'esecuzione di un'operazione con carico prolungato nel metodo activate rende l'app "bloccata" mentre l'operazione di caricamento è completata. Ad esempio, cosa succede se il mio carico era ora qualcosa di simile?

viewModel.activate = function() { 
    // All loads return a promise 
    var firstLoad = myService.loadFirstData(); 
    var secondLoad = myService.loadSecondData(); 
    var thirdLoad = myService.loadThirdDataWhichTakesAges(); 

    return $.when(firstLoad, secondLoad, thirdLoad).then(function (one, two, three) { 
     viewModel.one(one); 
     viewModel.two(two); 
     viewModel.three(three); 
    }); 
}; 

In questo scenario, l'URL viene aggiornato per riflettere pagina che viene caricata, ma il contenuto della pagina mostra ancora la pagina precedente (che è quello che intendo per "congela").

Idealmente, sarebbe bene se l'URL dovesse cambiare alla nuova pagina, e il contenuto della pagina dovrebbe mostrare anche la nuova pagina (anche se i dati per quella pagina non sono ancora stati restituiti). Quindi, al ripristino di ciascuna operazione di caricamento, la parte pertinente della pagina deve essere aggiornata quando i dati sono associati al modello di visualizzazione.

Esiste un modo consigliato per ottenere questo all'interno di Durandal?

La mia soluzione attuale è quella di kick-off il carico nel metodo activate, e quindi popolare i dati nel metodo viewAttached:

var loadPromise; 

viewModel.activate = function() { 
    // All loads return a promise 
    var firstLoad = myService.loadFirstData(); 
    var secondLoad = myService.loadSecondData(); 
    var thirdLoad = myService.loadThirdDataWhichTakesAges(); 

    loadPromise = $.when(firstLoad, secondLoad, thirdLoad); 

    // Don't return the promise - let activation proceed. 
}; 

viewModel.viewAttached = function() { 
    $.when(loadPromise).then(function (one, two, three) { 
     viewModel.one(one); 
     viewModel.two(two); 
     viewModel.three(three); 
    }); 
}; 

E sembra a lavorare, ma mi ricordo di aver letto da qualche parte che affidarsi a viewAttached non era una buona soluzione. Inoltre, non sono sicuro che ci sia un potenziale per una condizione di gara dal momento che sto permettendo che l'attivazione proceda.

Altri consigli?

+0

In generale, per le mie applicazioni SPA creo un div trasparente con "caricamento" di testo e gif. Questo div è sovrapposto alla pagina completa, in modo che gli utenti ricevano un feed back che l'applicazione sta cercando di fare qualcosa ma allo stesso tempo non dovrebbero essere in grado di fare clic su qualcos'altro o fare una sorta di manipolazione dei dati mentre la mia pagina è essere navigato. È possibile creare un servizio comune che qualsiasi vista può chiamare prima dell'avvio della navigazione e rimuovere il div dopo la fine della navigazione. – Yogesh

+0

Grazie a @ Yogesh, faccio qualcosa di simile, anche se cerco di evitare gli overlay di pagina completi perché non si comportano bene sui dispositivi mobili. – gerrod

risposta

8

Non è necessario restituire una promessa, ma in questo caso è necessario gestirla in questi collegamenti ad eliminazione diretta in modo da non essere vincolati a elementi non definiti. Puoi provare a sbarazzarti di quel "ritorno" in attivazione ma aggiungi una proprietà che indica se il modello è ancora in fase di caricamento. Qualcosa di simile a questo:

viewModel.isLoading = ko.observable(false); 
viewModel.activate = function() { 
    isLoading(true); 
    var loadPromise = myService.loadData(); 

    $.when(loadPromise).then(function (loadedData) { 
     viewModel.data(data); 
     isLoading(false); 
    }); 
}; 

E poi, a suo avviso, si può avere una sezione che si presenta quando vista è ancora carico e uno che si presenta quando il caricamento è fatto.Qualcosa come:

<div data-bind:"visible: isLoading()">Loading Data....</div> 
<div data-bind:"visible: !isLoading()">Put your regular view with bindings here. Loading is done so bindings will work.</div> 
+0

Non sono sicuro che ciò impedirà il problema, anche se i binding non si applicano correttamente. 'Visible:! IsLoading()' non impedisce l'applicazione di binding figlio (dove l'associazione 'if' fa perché i nodi figli vengono rimossi dal dom). Da quello che ho visto, i problemi si verificano se aggiorni gli osservabili di viewModel tra la chiamata 'activate' e la chiamata' viewAttached'. – gerrod

+0

Sulla rilettura della mia domanda sembra che non fossi abbastanza specifico in quello che stavo chiedendo - volevo sapere il modo migliore per evitare i problemi di associazione dei dati, piuttosto che il modo migliore di mostrare all'utente che un operazione di carico stava accadendo. Quindi ora ho una migliore comprensione della natura della tua risposta; quindi lo contrassegnerò come corretto! – gerrod

+0

Ottimo! Quindi non abbiamo bisogno di restituire la promessa al motore. –

0

Quale versione di Durandal stai usando? In Durandal 2.0.0pre ti sarà permesso NON restituire una promessa in activate in modo che la composizione della vista (senza dati) possa avvenire immediatamente.

È possibile considerare il refactoring viewModel.one ecc. In un modulo che restituisce una funzione di costruzione, in modo che ciascuno, due, tre siano responsabili del recupero dei propri dati. In questo modo le prime due chiamate non dovranno attendere il loadThirdDataWhichTakesAges. Ciò avrebbe senso in scenari in cui uno, due, tre non dipendono fortemente l'uno dall'altro.

+0

Il problema qui è che se uno/due/tre si risolvono tutti da qualche parte tra 'activate' e' viewAttached', si finisce con una race condition che rompe i binding. Controlla il link a cui ho fatto riferimento nella mia domanda per una spiegazione di ciò. Grazie per la tua risposta! – gerrod

0

Per riferimento; Ho postato una domanda simile sul Durandal Google Group (chiedendo in effetti se usare Activ e viewAttached in questo modo è un'idea OK) e ho ricevuto questa risposta da Rob Eisenberg:

Che probabilmente funzionerà. Il problema è che Knockout distruggerà le associazioni dati sugli elementi se le proprietà sono aggiornate e l'elemento non è attualmente nel documento. Ciò può accadere in base alla tempistica del codice asincrono. A causa del modo in cui la composizione ha funzionato in 1.x, ciò causerebbe problemi se non si restituisse la promessa dalla funzione di attivazione. Dovrebbe funzionare meglio in viewAttached, ma a seconda della natura della composizione, la vista potrebbe essere allegata alla sua genitrice, ma non ancora nel documento. Dipende dalla profondità della composizione. Quindi, potresti riscontrare problemi anche con questo se lo hai in un modulo profondamente composto. Sfortunatamente, non c'è un modo pulito a riguardo in Durandal 1.x a causa del comportamento a eliminazione diretta. In Durandal 2.x abbiamo rielaborato la composizione in modo che questo problema sia inesistente e che la promessa non sia più necessaria (anche se è ancora possibile farlo). Durandal 2.0 sarà rilasciato in circa due settimane .