Biorąc pod uwagę obliczoną właściwość
vm.checkedValueCount = ko.computed(function(){
var observables = getCurrentValues(); //an array of ko.observable[]
return _.filter(observables, function(v) { return v() }).length;
});
załóżmy, że getCurrentValues () może zwracać różne zestawy obserwabli, które są modyfikowane w innym miejscu w kodzie (i pochodzą z bardziej złożonej struktury niż obserwaableArray).
Muszę checkedValueCount
aktualizować kiedykolwiek
- zmienia się jedna z jego zależności
- getCurrentValues () zwraca inny zestaw obserwabli.
Problem polega na tym, że ko.computed
wydaje się zapamiętywać ostatnią zwróconą wartość i aktualizować tylko wtedy, gdy aktualizuje się zależność. Dotyczy to pierwszego przypadku, ale nie drugiego.
To, czego szukam, to sposób na wymuszenie ponownego uruchomienia checkValueCount. Coś, czego mogę użyć, na przykład:
changeCurrentValues();
vm.checkeValueCount.recalculate();
Mówiąc najprościej, biorąc pod uwagę, że mam
a = ko.computed(function() { return Math.random() })
jak wymusić a()
dwukrotne wywołanie, aby zwrócić różne wartości.
knockout.js
computed-observable
George Mauer
źródło
źródło
Odpowiedzi:
Zdałem sobie sprawę, że moja pierwsza odpowiedź pominęła Twój punkt widzenia i nie rozwiąże Twojego problemu.
Problem polega na tym, że komputer obliczony dokona ponownej oceny tylko wtedy, gdy pojawi się jakaś obserwowalna, która zmusza go do ponownej oceny. Nie ma natywnego sposobu na wymuszenie ponownej oceny obliczeń.
Możesz jednak obejść ten problem za pomocą hakerów, tworząc fałszywą obserwowalną wartość, a następnie informując subskrybentów, że się zmieniła.
(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()); }());
Oto Fiddle pokazujący to podejście
źródło
Istnieje metoda wymuszenia ponownego obliczenia wszystkich obserwabli w zależności od twojego:
źródło
getCurrentValues(); //an array of ko.observable[]
valueHasMutated
metodę. Nie różni się to zasadniczo od powyższej odpowiedzi Josha, prawda? W rzeczywistości zmusza cię to do wiedzieć, do czego się odwołuje i wiedzieć, że te odniesienia są obserwowalne (a nie obliczane).Ta odpowiedź jest koncepcyjnie taka sama, jak ta, którą podał @josh, ale przedstawiona jako bardziej ogólne opakowanie. Uwaga: ta wersja jest przeznaczona do obliczeń z możliwością zapisu.
Używam Typescript, więc najpierw dołączyłem definicję ts.d. Więc zignoruj tę pierwszą część, jeśli nie dotyczy ciebie.
Powiadomienie-zapisywalne-obliczone
Opakowanie dla zapisywalnego,
observable
które zawsze powoduje powiadomienie subskrybentów - nawet jeśli żadne obserwowalne nie zostały zaktualizowane w wynikuwrite
połączeniaWystarczy wymienić
function<T> (options: KnockoutComputedDefine<T>, context)
zefunction(options, context)
jeśli nie używać maszynopis.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); }
Głównym przypadkiem użycia jest sytuacja, gdy aktualizujesz coś, co w przeciwnym razie nie spowodowałoby zmiany w obserwowalnym, który jest „odwiedzany” przez
read
funkcję.Na przykład używam LocalStorage do ustawiania niektórych wartości, ale nie ma żadnej zmiany w żadnej obserwowalnej, aby wywołać ponowną ocenę.
hasUserClickedFooButton = ko.notifyingWritableComputed( { read: () => { return LocalStorageHelper.getBoolValue('hasUserClickedFooButton'); }, write: (v) => { LocalStorageHelper.setBoolValue('hasUserClickedFooButton', v); } });
Pamiętaj, że wszystko, co potrzebne do zmiany było
ko.computed
, abyko.notifyingWritableComputed
i to wszystko dba o siebie.Kiedy
hasUserClickedFooButton(true)
wywołuję, obserwowalny `` manekin '' jest zwiększany, zmuszając wszystkich subskrybentów (i ich subskrybentów) do uzyskania nowej wartości, gdy wartość w LocalStorage jest aktualizowana.(Uwaga: możesz pomyśleć, że
notify: 'always'
przedłużacz jest tutaj opcją - ale to coś innego).Istnieje dodatkowe rozwiązanie dla obliczonego obserwowalnego, które jest tylko do odczytu:
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();
Z @mbest komentarz https://github.com/knockout/knockout/issues/1019 .
źródło
Zakładam, że getCurrentValues () jest funkcją. Gdybyś mógł to zrobić, twój checkValueCount magicznie zacząłby działać.
Czy możesz sprawić, by getCurrentValues było obliczane zamiast funkcji?
źródło
ponieważ nie ma prostego sposobu, aby wymusić aktualizację obliczonego, utworzyłem obserwowalny o nazwie toForceComputedUpdate i nazwałem go w ramach funkcji obliczeniowej, aby komputer obliczony słuchał jego aktualizacji, a następnie, aby wymusić aktualizację, nazywam to w ten sposób do ForceComputedUpdate (Math .losowy)
źródło