Dada una propiedad calculada
vm.checkedValueCount = ko.computed(function(){
var observables = getCurrentValues(); //an array of ko.observable[]
return _.filter(observables, function(v) { return v() }).length;
});
supongamos que getCurrentValues () puede devolver diferentes conjuntos de observables que se modifican en otra parte del código (y proviene de una estructura más compleja que un observableArray).
Necesito checkedValueCount
actualizar cuando sea
- una de sus dependencias cambia
- getCurrentValues () devuelve un conjunto diferente de observables.
El problema es que ko.computed
parece memorizar el último valor devuelto y solo se actualiza cuando se actualiza una dependencia. Esto maneja el primer caso pero no el último.
Lo que estoy buscando es una forma de forzar la ejecución de checkValueCount. Algo que pueda usar como:
changeCurrentValues();
vm.checkeValueCount.recalculate();
En pocas palabras, dado que tengo
a = ko.computed(function() { return Math.random() })
¿Cómo puedo forzar la invocación a()
dos veces para devolver diferentes valores?
knockout.js
computed-observable
George Mauer
fuente
fuente
Respuestas:
Me di cuenta de que mi primera respuesta no entendía su punto y no resolvería su problema.
El problema es que un calculado solo se reevaluará si hay algo observable que lo obligue a reevaluarse. No existe una forma nativa de forzar una reevaluación de un calculado.
Sin embargo, puede evitar esto con algunos piratas informáticos creando un valor observable ficticio y luego diciéndoles a sus suscriptores que ha cambiado.
(function() { var vm = function() { var $this = this; $this.dummy = ko.observable(); $this.curDate = ko.computed(function() { $this.dummy(); return new Date(); }); $this.recalcCurDate = function() { $this.dummy.notifySubscribers(); }; }; ko.applyBindings(new vm()); }());
Aquí hay un violín que muestra este enfoque.
fuente
Existe un método para forzar el recálculo de todos los observables dependiendo del suyo:
fuente
getCurrentValues(); //an array of ko.observable[]
valueHasMutated
método. Esto no es realmente fundamentalmente diferente de la respuesta de Josh anterior, ¿verdad? De hecho, lo obliga a saber qué se hace referencia y saber que esas referencias son observables (y no calculadas).Esta respuesta es conceptualmente la misma que la que dio @josh, pero presentada como una envoltura más genérica. Nota: esta versión es para un cálculo "escribible".
Estoy usando TypeScript, así que incluí primero la definición ts.d. Así que ignore esta primera parte si no es relevante para usted.
Notificando-escribible-calculado
Un contenedor para una escritura
observable
que siempre hace que los suscriptores sean notificados, incluso si no se actualizaron observables como resultado de lawrite
llamada.Simplemente reemplácelo
function<T> (options: KnockoutComputedDefine<T>, context)
confunction(options, context)
si no usa Typecript.ko.notifyingWritableComputed = function<T> (options: KnockoutComputedDefine<T>, context) { var _notifyTrigger = ko.observable(0); var originalRead = options.read; var originalWrite = options.write; // intercept 'read' function provided in options options.read = () => { // read the dummy observable, which if updated will // force subscribers to receive the new value _notifyTrigger(); return originalRead(); }; // intercept 'write' function options.write = (v) => { // run logic provided by user originalWrite(v); // force reevaluation of the notifyingWritableComputed // after we have called the original write logic _notifyTrigger(_notifyTrigger() + 1); }; // just create computed as normal with all the standard parameters return ko.computed(options, context); }
El caso de uso principal para esto es cuando está actualizando algo que de otra manera no desencadenaría un cambio en un observable que es 'visitado' por la
read
función.Por ejemplo, estoy usando LocalStorage para establecer algunos valores, pero no hay ningún cambio en ningún observable para activar la reevaluación.
hasUserClickedFooButton = ko.notifyingWritableComputed( { read: () => { return LocalStorageHelper.getBoolValue('hasUserClickedFooButton'); }, write: (v) => { LocalStorageHelper.setBoolValue('hasUserClickedFooButton', v); } });
Tenga en cuenta que todo lo que necesitaba era un cambio
ko.computed
deko.notifyingWritableComputed
y entonces todo se cuida solo.Cuando llamo
hasUserClickedFooButton(true)
, el observable 'ficticio' se incrementa, lo que obliga a los suscriptores (y sus suscriptores) a obtener el nuevo valor cuando se actualiza el valor en LocalStorage.(Nota: puede pensar que el
notify: 'always'
extensor es una opción aquí, pero eso es algo diferente).Existe una solución adicional para un observable calculado que solo se puede leer:
ko.forcibleComputed = function(readFunc, context, options) { var trigger = ko.observable().extend({notify:'always'}), target = ko.computed(function() { trigger(); return readFunc.call(context); }, null, options); target.evaluateImmediate = function() { trigger.valueHasMutated(); }; return target; }; myValue.evaluateImmediate();
Del comentario de @mbest https://github.com/knockout/knockout/issues/1019 .
fuente
Supongo que getCurrentValues () es una función. Si pudiera convertirlo en un calculado, su checkValueCount simplemente comenzaría a funcionar mágicamente.
¿Puede hacer que getCurrentValues sea un cálculo en lugar de una función?
fuente
Dado que no hay una forma directa de forzar la actualización de un calculado, he creado un observable llamado toForceComputedUpdate, y lo llamé dentro de la función calculada para que el calculado escuche su actualización, luego para forzar la actualización lo llamo así para ForceComputedUpdate (Math .aleatorio)
fuente