webentwicklung-frage-antwort-db.com.de

Erzwinge die Ausführung einer berechneten Eigenschaftsfunktion

Gegeben eine berechnete Eigenschaft

vm.checkedValueCount = ko.computed(function(){
  var observables = getCurrentValues();  //an array of ko.observable[]
  return _.filter(observables, function(v) { return v() }).length;
});

angenommen, getCurrentValues ​​() kann verschiedene Mengen von Observablen zurückgeben, die an anderer Stelle im Code geändert wurden (und aus einer komplexeren Struktur als observableArray stammen).

Ich brauche checkedValueCount, um zu aktualisieren, wann immer

  • eine seiner Abhängigkeiten ändert sich
  • getCurrentValues ​​() gibt eine andere Menge von Observablen zurück.

Das Problem ist, dass ko.computed Sich den zuletzt zurückgegebenen Wert zu merken scheint und nur aktualisiert wird, wenn eine Abhängigkeit aktualisiert wird. Dies behandelt den ersten Fall, aber nicht den letzteren.

Was ich suche, ist eine Möglichkeit, checkedValueCount zum erneuten Ausführen zu zwingen. Etwas was ich benutzen kann wie:

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

Am einfachsten ausgedrückt, vorausgesetzt, ich habe

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

wie kann ich den zweimaligen Aufruf von a() erzwingen, um unterschiedliche Werte zurückzugeben?.

77
George Mauer

Ich habe festgestellt, dass meine erste Antwort Ihren Standpunkt verfehlt hat und ich werde Ihr Problem nicht lösen.

Das Problem ist, dass ein berechneter Wert nur dann neu bewertet wird, wenn es einige beobachtbare Werte gibt, die ihn zur Neubewertung zwingen. Es gibt keine systemeigene Möglichkeit, einen Computer zur Neubewertung zu zwingen.

Sie können dieses Problem jedoch umgehen, indem Sie einen beobachtbaren Dummy-Wert erstellen und dann den Abonnenten mitteilen, dass er geändert wurde.

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

}());​

Hier ist eine Geige zeigt diesen Ansatz

113
Josh

Es gibt eine Methode um die Neuberechnung aller Observablen zu erzwingen, abhängig von Ihrer:

getCurrentValues.valueHasMutated()
8
Rustam

Diese Antwort ist konzeptionell dieselbe wie die von @josh, wird jedoch als allgemeinerer Wrapper dargestellt. Hinweis: Diese Version ist für ein 'beschreibbares' Gerät berechnet.

Da ich TypeScript verwende, habe ich zuerst die ts.d-Definition eingefügt. Ignorieren Sie diesen ersten Teil, wenn er für Sie nicht relevant ist.

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


Benachrichtigung-beschreibbar-berechnet

Ein Wrapper für einen beschreibbaren observable, der immer dazu führt, dass Abonnenten benachrichtigt werden - selbst wenn infolge des Aufrufs von write keine Observables aktualisiert wurden

Ersetzen Sie einfach function<T> (options: KnockoutComputedDefine<T>, context) durch function(options, context), wenn Sie TypeScript nicht verwenden.

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

Der Hauptanwendungsfall hierfür ist, wenn Sie etwas aktualisieren, das ansonsten keine Änderung in einer Beobachtungsdatei auslösen würde, die von der Funktion read 'besucht' wird.

Zum Beispiel verwende ich LocalStorage, um einige Werte festzulegen, aber es gibt keine Änderung an beobachtbaren Werten, um eine Neubewertung auszulösen.

hasUserClickedFooButton = ko.notifyingWritableComputed(
{
    read: () => 
    {
        return LocalStorageHelper.getBoolValue('hasUserClickedFooButton');
    },
    write: (v) => 
    {
        LocalStorageHelper.setBoolValue('hasUserClickedFooButton', v);        
    }
});

Beachten Sie, dass ich nur ko.computed In ko.notifyingWritableComputed Ändern musste und dann alles für sich selbst sorgt.

Wenn ich hasUserClickedFooButton(true) aufrufe, wird die beobachtbare 'Dummy'-Variable inkrementiert, sodass alle Abonnenten (und deren Abonnenten) gezwungen werden, den neuen Wert abzurufen, wenn der Wert in LocalStorage aktualisiert wird.

(Hinweis: Möglicherweise ist der Extender notify: 'always' Hier eine Option - aber das ist etwas anderes).


Es gibt eine zusätzliche Lösung für ein berechnetes Observable, das nur lesbar ist:

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

Von @mbest Kommentar https://github.com/knockout/knockout/issues/1019 .

4
Simon_Weaver

angenommen, getCurrentValues ​​() kann verschiedene Mengen von Observablen zurückgeben, die an anderer Stelle im Code geändert wurden

Ich nehme an, getCurrentValues ​​() ist eine Funktion. Wenn Sie es zu einem berechneten Wert machen könnten, würde Ihr checkedValueCount auf magische Weise funktionieren.

Können Sie getCurrentValues ​​anstelle einer Funktion berechnen lassen?