2012-07-19 12 views
22

Tengo un modelo de vista complejo que es un par de cientos de líneas de código JavaScript con una buena cantidad de propiedades observables, propiedades observables computables, propiedades y funciones observables computables escribibles. Así que manejar esto es un gran desafío.¿Hay alguna manera de decirle a un knockout que espere para recalcular los valores calculados hasta después de que se haya definido el modelo de vista?

Un problema molesto con el que he tenido que lidiar es que las observaciones calculadas se calculan inmediatamente justo cuando se define. Por lo tanto, el uso de variables que aún no se han definido en el modelo de vista en el momento de definir lo observable conduce a errores que indican que la variable no se ha definido. Es ... simplemente más tarde en el archivo.

Aquí está un ejemplo artificioso:

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

Usando ViewModel1 funcionará sin problemas. En el punto fullName se define, firstName y lastName se define para que funcione como se esperaba. El uso de ViewModel2 no funcionará. Habría un error en la función calculada que indica que firstName no está definido.

Lo que he estado haciendo hasta ahora era asegurarme de que todos los observables calculados se definan después de que se hayan definido todas las variables dependientes. El problema con esto es que al hacerlo, las cosas se definen en lugares aparentemente aleatorios cuando prefiero mantener las variables relacionadas muy juntas.

Una buena solución que he encontrado es definir un conjunto observable de "inicialización" en true y hacer que todos los observables computados prueben si todavía está inicializando y calcular y devolver el valor cuando no lo esté. De esta forma, no se realizarán los intentos de acceder a la variable actualmente indefinida.

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

Pero esto no será muy práctico en mi caso sin embargo. Tengo muchos observables computados, por lo que hacer esto en todos ellos lo hará muy hinchado, recuerda que tengo muchos de estos. Estrangularlo no parece hacer la diferencia.

¿Hay alguna manera de decirle a un knockout que espere antes de intentar calcular los valores de los observables calculados? ¿O hay una mejor manera de estructurar mi código para lidiar con esto?

Probablemente podría hacer algunas funciones auxiliares para administrar la lógica de inicialización, pero aún tendría que alterar todas las definiciones observables calculadas. Supongo que puedo hacer un parche de mono para agregar esta lógica de inicialización ya que no estoy seguro de que el nocaut tenga esas opciones que podría hacer. Miré la fuente de observables calculados antes, pero no sé si ya existe alguna configuración en otro lugar.

jsfiddle demo

Respuesta

37

observables calculados aceptan una opción deferEvaluation que impide la evaluación inicial del suceso hasta que algo en realidad trata de recuperar el valor de computada.

Se podría definir como:

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

simplemente para la corrección, se podría especificar también se siente:

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

O usted podría envolverlo como:

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, bueno ahí está. Es un poco detallado para mi gusto, pero tendrá que hacer. –

+0

Podría envolverlo para hacerlo menos detallado. Agregado a la respuesta. Eso debería funcionar para computeds normales y escribibles. –

+0

Sí, eso fue lo que terminé haciendo, aunque lo que tienes es más limpio que mi intento. :) –

Cuestiones relacionadas