79 votes

Forcer l'exécution d'une fonction de propriété calculée

Étant donné une propriété calculée

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

suppose que getCurrentValues() peut renvoyer différents ensembles d'observables qui sont modifiés ailleurs dans le code (et provient d'une structure plus complexe qu'un observableArray).

J'ai besoin checkedValueCount à mettre à jour chaque fois que

  • l'une de ses dépendances change
  • getCurrentValues() renvoie un ensemble différent d'observables.

Le problème est que ko.computed semble mémoriser la dernière valeur renvoyée et ne la mettre à jour que lorsqu'une dépendance est mise à jour. Ceci gère le premier cas mais pas le second.

Ce que je cherche, c'est un moyen de forcer checkedValueCount à s'exécuter à nouveau. Quelque chose que je puisse utiliser comme :

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

Plus simplement, étant donné que j'ai

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

Comment puis-je forcer l'invocation de a() deux fois pour renvoyer des valeurs différentes.

115voto

Josh Points 23923

Je me suis rendu compte que ma première réponse n'a pas tenu compte de votre point de vue et qu'elle ne résoudra pas votre problème.

Le problème est qu'un calculateur ne réévaluera que s'il existe une observable qui l'oblige à le faire. Il n'y a pas de moyen natif de forcer un calculateur à réévaluer.

Il est toutefois possible de contourner ce problème en créant une valeur observable factice et en indiquant à ses abonnés qu'elle a changé.

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

}());​

Voici un violon montrant cette approche

8voto

Rustam Points 400

Il y a une méthode pour forcer le recalcul de toutes les observables en fonction de la vôtre :

getCurrentValues.valueHasMutated()

4voto

Simon_Weaver Points 31141

Cette réponse est conceptuellement la même que celle donnée par @josh, mais elle est présentée sous une forme plus générique. Note : cette version est pour un calcul 'inscriptible'.

J'utilise Typescript et j'ai donc inclus la définition ts.d en premier. Ignorez donc cette première partie si elle ne vous concerne pas.

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

Notification de l'écriture calculée

Une enveloppe pour un fichier observable que toujours entraîne la notification des abonnés, même si aucun observable n'a été mis à jour à la suite de l'opération write appel

Il suffit de remplacer function<T> (options: KnockoutComputedDefine<T>, context) con function(options, context) si vous n'utilisez pas 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);
}

Le principal cas d'utilisation est la mise à jour d'un élément qui ne déclencherait pas de changement dans un observable "visité" par la fonction read fonction.

Par exemple, j'utilise LocalStorage pour définir certaines valeurs, mais aucun observable n'est modifié pour déclencher une réévaluation.

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

Notez que tout ce que j'ai eu besoin de changer était ko.computed a ko.notifyingWritableComputed et tout s'arrangera tout seul.

Lorsque j'appelle hasUserClickedFooButton(true) l'observable "factice" est alors incrémenté, ce qui oblige tous les abonnés (et leurs abonnés) à obtenir la nouvelle valeur lorsque la valeur de LocalStorage est mise à jour.

(Note : vous pouvez penser que le notify: 'always' est une option ici - mais c'est quelque chose de différent).


Il existe une solution supplémentaire pour une observable calculée qui n'est que lisible :

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

Extrait du commentaire de @mbest https://github.com/knockout/knockout/issues/1019 .

1voto

Judah Himango Points 27365

suppose que getCurrentValues() peut renvoyer différents ensembles d'observables qui sont modifiés ailleurs dans le code

Je suppose que getCurrentValues() est une fonction. Si vous pouviez en faire une fonction calculée, votre checkedValueCount commencerait à fonctionner comme par magie.

Pouvez-vous faire en sorte que getCurrentValues soit un calcul au lieu d'une fonction ?

0voto

yoel neuman Points 105

Comme il n'y a pas de moyen direct de forcer la mise à jour d'un calcul, j'ai créé un observable nommé toForceComputedUpdate, et je l'ai appelé dans la fonction de calcul pour que le calcul écoute sa mise à jour, puis pour forcer la mise à jour, je l'appelle comme ceci toForceComputedUpdate(Math.random)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X