Wymuś uruchomienie obliczonej funkcji właściwości

80

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ę checkedValueCountaktualizować kiedykolwiek

  • zmienia się jedna z jego zależności
  • getCurrentValues ​​() zwraca inny zestaw obserwabli.

Problem polega na tym, że ko.computedwydaje 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.

George Mauer
źródło
Zobacz moją zaktualizowaną odpowiedź „przepisaną”.
Josh

Odpowiedzi:

116

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

Josh
źródło
Ach, próbowałem tego, ale musiałem źle to podłączyć. Wygląda to na zepsuty, ale dobrze (i wystarczająco dobrze, że mogę przedłużyć ko. Obliczone, aby pozwolić mu działać
George Mauer
Jak zawsze prostsze jest najlepsze.
Henry Rodriguez
1
Jest to przydatne, gdy zmieniasz dużą tablicę danych, których nie można obserwować, a po zmianach część danych musi zostać wyświetlona.
Marek Bar
1
Korzystanie z notifySubscribers () jest dobre. Lepsze niż to, co robiłem, budując liczbę losową i ustawiając ją na wartość dummy ()
efreed
9

Istnieje metoda wymuszenia ponownego obliczenia wszystkich obserwabli w zależności od twojego:

getCurrentValues.valueHasMutated()
Rustam
źródło
9
Obliczeni nie mają tej metody
George Mauer
getCurrentValues(); //an array of ko.observable[]
Rustam
Rozumiem. Mówisz, że znajdź obserwowalny, który jest zależnością obliczonego i wywołaj jego valueHasMutatedmetodę. 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).
George Mauer
2
niemniej jednak dobrze jest wspomnieć o tej funkcji, ponieważ pozostaje ona opcją dla czytelników, chociaż być może ktoś mógłby zaktualizować zaakceptowaną odpowiedź, aby odnotować przypadki, w których ta funkcja może być zamieniona z notifySubscribers () i czy jest jakaś kara lub korzyść tak czy inaczej.
Shaun Wilson
4

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.

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

Powiadomienie-zapisywalne-obliczone

Opakowanie dla zapisywalnego, observablektóre zawsze powoduje powiadomienie subskrybentów - nawet jeśli żadne obserwowalne nie zostały zaktualizowane w wyniku writepołączenia

Wystarczy wymienić function<T> (options: KnockoutComputedDefine<T>, context)ze function(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 readfunkcję.

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, aby ko.notifyingWritableComputedi 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 .

Simon_Weaver
źródło
Co masz na myśli, mówiąc, że nie ma wartości? Czy to zadziałało przed przejściem na to. Czy korzystasz z odroczonych aktualizacji (ustawienie globalne). Właśnie zdałem sobie sprawę, że może wystąpić konflikt, jeśli spodziewasz się, że będziesz w stanie zaktualizować obserwowalne, a następnie natychmiast użyć wartości. Jeśli tak, spróbuj dodać ko.tasks.runEarly () do mojej metody.
Simon_Weaver,
1

załóżmy, że getCurrentValues ​​() może zwracać różne zestawy obserwabli, które są modyfikowane w innym miejscu kodu

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?

Judah Gabriel Himango
źródło
Zbytnio upraszczam mój rzeczywisty scenariusz, ale na pewno, powiedzmy, że tak jest (co by nie miało miejsca, gdyby trzeba było na przykład wziąć parametry) - nie widzę, jak to by miało jakąkolwiek różnicę. Wewnątrz getCurrentValues ​​() dzielę i dzielę inne dane, aby określić, które węzły widoku drzewa mają zostać zwrócone. problem polega na tym, że funkcja może zwracać różne obserwowalne wartości i potrzebuję obliczonego, aby to odebrać. Zasadniczo dokładnie to, o czym mówią doktorzy z dynamicznymi zależnościami, ale z komplikacjami dodawania ich w locie
George Mauer
Miałoby to znaczenie, gdyby „krojenie i krojenie” opierało się na obserwablach. Na przykład, powiedzmy, że twoje cięcie i kostkowanie to węzły, które mają .IsChecked () == true. Miałbyś wtedy obliczoną nazwę .currentValues ​​(), która oceniłaby wszystkie węzły i zwróciłaby te z .IsChecked (). Czy krojenie i w kostkę można wykonać na obserwowalnych właściwościach?
Judah Gabriel Himango
Naprawdę nie jestem pewien, co mówisz Judah, każda inwokacja nie może być ko obserwowalna w moim przypadku - ale co ważniejsze, nie widzę, jakie to ma znaczenie, nokaut nie ma żadnego wpływu na jakie domknięcia dzwoniłeś (nie sprawdzałem, ale jeśli javascript nie przeszedł całkowicie do krainy seplenienia, jest to niemożliwe). Wszystko, co można zrobić, to w najlepszym przypadku śledzić wszelkie wywoływane obserwable.
George Mauer
-1

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)

yoel neuman
źródło