javascript knockout.js

javascript - ¿Hay alguna manera de decirle a un knockout que espere para volver a calcular los valores calculados hasta después de que se haya definido el modelo de vista?



knockout.js (1)

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 calculadas, propiedades y funciones observables computables escribibles. Así que manejar esto es un gran desafío.

Un problema molesto con el que he tenido que lidiar es que los observables calculados se calculan inmediatamente cuando lo defines. 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í hay un ejemplo artificial:

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

Usar ViewModel1 funcionará sin problemas. En el punto en que se define fullName se define firstName y lastName para que funcione como se espera. Usar 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 fue asegurarme de que todos los observables computados 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 "inicializante" como true y hacer que todos los observables computados prueben si aún se 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 marcar 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


Los observables computados aceptan una opción deferEvaluation que evita que la evaluación inicial suceda hasta que algo realmente intente recuperar el valor calculado.

Lo definirías así:

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

Para completar, también podría especificarlo así:

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

O podrías 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); };