2012-07-19 7 views
22

Ho un modello di vista complesso che è un paio di centinaia di righe di codice javascript con una buona quantità di proprietà osservabili, proprietà osservabili calcolate, proprietà osservabili elaborabili e funzioni scritte. Quindi gestire questo è un po 'una sfida.C'è un modo per dire a knockout di attendere per ricalcolare i valori calcolati fino a dopo la definizione del modello di vista?

Un fastidioso problema che ho dovuto affrontare è che gli osservabili calcolati vengono calcolati immediatamente nel momento in cui viene definito. Quindi, utilizzando variabili che non sono state ancora definite nel modello di vista al punto di definire il lead osservabile in errori, affermando che la variabile non è stata definita. È ... solo più tardi nel file.

Ecco un esempio inventato:

function ViewModel1​(args) { 
    var self = this; 

    self.firstName = ko.observable(args.firstName); 
    self.lastName = ko.observable(args.lastName); 
    self.fullName = ko.computed(function() { 
     return self.firstName() + ' ' + self.lastName(); 
    }); 
} 

function ViewModel2​(args) { 
    var self = this; 

    self.fullName = ko.computed(function() { 
     // uh oh, where's firstName? 
     return self.firstName() + ' ' + self.lastName(); 
    }); 
    self.firstName = ko.observable(args.firstName); 
    self.lastName = ko.observable(args.lastName); 
} 

Utilizzando ViewModel1 funzionerà senza problemi. Al punto fullName è definito, firstName e lastName è definito in modo che funzioni come previsto. L'utilizzo di ViewModel2 non funzionerà. Ci sarebbe un errore nella funzione calcolata che afferma che firstName non è definito.

Quello che ho fatto fino ad ora è stato quello di garantire che tutti gli osservabili calcolati siano definiti dopo che tutte le variabili dipendenti sono state definite. Il problema con questo è che nel fare ciò, le cose sono definite in posti apparentemente casuali quando preferirei mantenere le variabili correlate definite vicine.

Una soluzione bello che mi è venuta in mente è quello di definire un "inizializzazione" insieme osservabile a true e fare tutti i test osservabili calcolato se è ancora l'inizializzazione e calcolare e restituire il valore quando non lo è. In questo modo, i tentativi di accedere alla variabile attualmente indefinita non saranno fatti.

function ViewModel3(args) { 
    var self = this; 
    var initializing = ko.observable(true); 

    self.fullName = ko.computed(function() { 
     if (!initializing()) { 
      return self.firstName() + ' ' + self.lastName(); 
     } 
    }); 
    self.firstName = ko.observable(args.firstName); 
    self.lastName = ko.observable(args.lastName); 

    initializing(false); 
} 

Ma questo non sarà molto pratico nel mio caso comunque. Ho molti osservabili calcolati, quindi farlo in tutti loro lo renderà molto gonfio, ricorda che ne ho molti di questi. Limitare non sembra fare la differenza.

C'è un modo per dire aspettare ad eliminazione diretta prima di provare a calcolare i valori degli osservabili calcolati? O c'è un modo migliore per strutturare il mio codice per far fronte a questo?

Probabilmente potrei fare alcune funzioni di supporto per gestire la logica di inizializzazione, ma dovrei comunque modificare tutte le definizioni osservabili calcolate. Suppongo di poter scovare la patch knockout per aggiungere questa logica inizializzante, dato che non mi risulta che knockout abbia opzioni del genere che potrei fare. Ho già esaminato la fonte degli osservabili calcolati, ma non so se ci sia già un'impostazione altrove.

jsfiddle demo

risposta

37

osservabili calcolate accettano un'opzione deferEvaluation che impedisce la valutazione iniziale accada fino a quando qualcosa in realtà tenta di recuperare il valore del calcolato.

Si potrebbe definirlo come:

self.fullName = ko.computed({ 
    read: function() { 
     return self.firstName() + " " + self.lastName(); 
    }, 
    deferEvaluation: true 
}); 

Solo per completezza, si potrebbe anche indicare le cose come:

this.fullName = ko.computed(function() { 
     return this.firstName() + " " + this.lastName(); 
}, this, { deferEvaluation: true }); 

Oppure si potrebbe avvolgere le cose come:

ko.deferredComputed = function(evaluatorOrOptions, target, options) { 
    options = options || {}; 

    if (typeof evaluatorOrOptions == "object") { 
     evaluatorOrOptions.deferEvaluation = true; 
    } else { 
     options.deferEvaluation = true; 
    } 

    return ko.computed(evaluatorOrOptions, target, options); 
}; 
+0

Ah bene Eccolo. È un po 'prolisso per i miei gusti, ma dovrà fare. –

+0

È possibile eseguire il wrapping per renderlo meno dettagliato. Aggiunto alla risposta. Questo dovrebbe funzionare per i calcoli normali e scrivibili. –

+0

Sì, questo è quello che ho finito per fare, anche se quello che hai è più pulito del mio tentativo. :) –

Problemi correlati