w3schools knockoutjs knockout data applybindings knockout.js computed-observable

knockout.js - knockoutjs - Forzar una función de propiedad calculada para ejecutar



knockoutjs variables (5)

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 otras partes del código (y proviene de una estructura más compleja que un array observable).

Necesito checkedValueCount para actualizar cada vez

  • 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 segundo.

Lo que estoy buscando es una forma de forzar checkValueCount para volver a ejecutar. Algo que puedo usar como:

changeCurrentValues(); vm.checkeValueCount.recalculate();

En pocas palabras, dado que tengo

a = ko.computed(function() { return Math.random() })

¿Cómo puedo forzar invocar a() dos veces para devolver diferentes valores?


supongamos que getCurrentValues ​​() puede devolver diferentes conjuntos de observables que se modifican en otro lugar en el código

Supongo que getCurrentValues ​​() es una función. Si pudieras calcularlo, tu valor de CheckValue simplemente comenzaría a funcionar mágicamente.

¿Se puede hacer que getCurrentValues ​​sea un cálculo en lugar de una función?


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 computable ''escribible''.

Estoy usando Typescript, así que he incluido la definición de ts.d primero. Así que ignora esta primera parte si no es relevante para ti.

interface KnockoutStatic { notifyingWritableComputed<T>(options: KnockoutComputedDefine<T>, context ?: any): KnockoutComputed<T>; }


Notificando-escribible-computado

Un contenedor para un observable escribible que siempre causa que se notifique a los suscriptores, incluso si no se actualizaron observables como resultado de la llamada de write

Simplemente reemplace la function<T> (options: KnockoutComputedDefine<T>, context) con la function(options, context) si no usa Typescript.

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 provocaría un cambio en un observable que es ''visitado'' por la función de read .

Por ejemplo, estoy usando LocalStorage para establecer algunos valores, pero no hay ningún cambio en cualquier 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 para cambiar fue ko.computed a ko.notifyingWritableComputed y luego todo se ko.notifyingWritableComputed solo.

Cuando llamo hasUserClickedFooButton(true) , el observable "ficticio" se incrementa forzando a los suscriptores (y sus suscriptores) a obtener el nuevo valor cuando se actualiza el valor en LocalStorage.

(Nota: puede pensar que notify: ''always'' extensor notify: ''always'' es una opción aquí, pero eso es algo diferente).

Hay 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();

Desde @mbest comment https://github.com/knockout/knockout/issues/1019 .


Hay un método para forzar el recálculo de todos los observables que dependen del suyo:

getCurrentValues.valueHasMutated()


Me di cuenta de que mi primera respuesta erró su punto, y no resolverá su problema.

El problema es que un cálculo solo volverá a evaluar si hay algún observable que lo obligue a reevaluar. No hay una forma nativa de forzar un cálculo para volver a evaluar.

Sin embargo, puede evitar esto con algunos hackers creando un valor ficticio observable y luego diciéndole 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


dado que no hay una forma directa de forzar la actualización de un computed, he creado un elemento visible llamado toForceComputedUpdate, y lo llamé dentro de la función calculada para que el computador escuche su actualización, luego para forzar la actualización lo llamo así aForceComputedUpdate (Math .aleatorio)