Примусово запустити обчислену функцію властивостей


80

Дано обчислювану властивість

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

припустимо, getCurrentValues ​​() може повертати різні набори спостережуваних, які модифіковані в інших місцях коду (і походять із більш складної структури, ніж observableArray).

Мені потрібно checkedValueCountоновлювати щоразу

  • одна з його залежностей змінюється
  • getCurrentValues ​​() повертає інший набір спостережуваних.

Проблема в тому, що, ko.computedздається, запам'ятовує останнє повернуте значення і оновлюється лише тоді, коли залежність оновлюється. Це стосується першого випадку, але не останнього.

Те, що я шукаю, - це спосіб змусити checkValueCount до повторного запуску. Щось, що я можу використовувати, наприклад:

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

Простіше кажучи, враховуючи, що я маю

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

як я можу змусити викликати a()двічі, щоб повернути різні значення.


Дивіться мою оновлену "переписану" відповідь.
Джош

Відповіді:


116

Я зрозумів, що моя перша відповідь пропустила вашу думку і не вирішить вашу проблему.

Проблема полягає в тому, що обчислений буде переоцінювати лише тоді, коли є якесь спостережуване, що змушує його переоцінити. Немає рідного способу змусити обчисленого переоцінити.

Тим не менш, ви можете обійти це з деяким хакерством, створивши фіктивне спостережуване значення, а потім повідомивши своїм передплатникам, що воно змінилося.

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

}());​

Ось скрипка, що демонструє цей підхід


Ах, я спробував це, але, мабуть, неправильно підключив. Це виглядає хакі, але добре (і досить добре, щоб я міг розширити ko.computed, щоб дозволити йому працювати
Джордж Мауер

Як завжди, простіше - найкраще.
Генрі Родрігес,

1
Це корисно, коли ви змінюєте великий масив даних, який неможливо спостерігати, і після змін частина даних повинна відображатися.
Marek Bar

1
Ваше використання notifySubscribers () є хорошим. Краще, ніж те, що я робив, будуючи випадкове число і встановлюючи це значення dummy ()
efreed

9

Існує метод примусового перерахунку всіх спостережуваних залежно від вашого:

getCurrentValues.valueHasMutated()

9
У комп’ютерів цього методу немає
Джордж Мауер,

getCurrentValues(); //an array of ko.observable[]
Рустам

О Я бачу. Ви говорите: знайти спостережуване, яке є залежністю обчислюваного, і назвати його valueHasMutatedметодом. Це насправді принципово не відрізняється від відповіді Джоша вище, чи не так? Насправді це змушує вас знати, на що посилається, і знати, що ці посилання можна спостерігати (і не обчислювати).
Джордж Мауер

2
все ж добре згадати цю функцію, оскільки вона залишається опцією для читачів, хоча, можливо, хтось міг би оновити прийняту відповідь, щоб відзначити випадки, коли цю функцію можна поміняти за допомогою notifySubscribers (), і якщо в будь-якому випадку є якісь штрафи чи переваги.
Шон Вільсон

4

Ця відповідь концептуально така ж, як і @josh, але представлена ​​як більш загальна обгортка. Примітка: ця версія призначена для обчислювального запису.

Я використовую Typescript, тому спочатку включив визначення ts.d. Тож ігноруйте цю першу частину, якщо вона не стосується вас.

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

Повідомлення-запис-обчислення

Обгортка для запису, observableяка завжди призводить до сповіщення передплатників - навіть якщо в результатіwrite дзвінка

Просто замініть function<T> (options: KnockoutComputedDefine<T>, context)на, function(options, context)якщо ви не використовуєте 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);
}

Основним випадком використання для цього є те, коли ви оновлюєте щось, що інакше не викликало б змін у спостережуваному, яке "відвідує" readфункція.

Наприклад, я використовую LocalStorage для встановлення деяких значень, але жодних спостережуваних змін не викликає, щоб викликати переоцінку.

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

Зверніть увагу, що все, що мені потрібно було змінити, - ko.computedце, ko.notifyingWritableComputedі тоді все піклується про себе.

Коли я дзвоню hasUserClickedFooButton(true) тоді спостережувана "фіктивна" збільшується, змушуючи будь-яких абонентів (та їхніх абонентів) отримувати нове значення, коли значення в LocalStorage оновлюється.

(Примітка: ви можете подумати, що notify: 'always'розширювач тут є варіантом - але це щось інше).


Існує додаткове рішення для обчислюваного спостережуваного, яке можна лише прочитати:

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

З коментаря @mbest https://github.com/knockout/knockout/issues/1019 .


Що ви маєте на увазі, що немає цінності? Це працювало до переходу на це. Чи використовуєте ви відкладені оновлення (загальне налаштування). Просто зрозумів, що може виникнути конфлікт, якщо ви очікуєте, що зможете оновити спостережуване, а потім негайно використати значення. Якщо так, спробуйте додати ko.tasks.runEarly () до мого методу.
Simon_Weaver

1

припустимо, getCurrentValues ​​() може повертати різні набори спостережуваних, які змінені в інших місцях коду

Я припускаю, що getCurrentValues ​​() є функцією. Якби ви могли зробити це обчислюваним, ваш checkValueCount просто магічно почав би працювати.

Чи можете ви зробити так, щоб getCurrentValues ​​обчислювався замість функції?


Я дуже спрощую свій реальний сценарій, але впевнений, припустимо, що це може бути (чого не було б, якби йому потрібно, наприклад, брати параметри) - я не бачу, як це може щось змінити. Усередині getCurrentValues ​​() Я нарізаю та нарізаю інші дані, щоб визначити, які вузли подання дерева потрібно повернути. проблема полягає в тому, що функція може повертати різні спостережувані значення, і мені потрібне обчислюване, щоб її визначити. В основному саме те, про що говорять документи з динамічними залежностями, але з ускладненням їх додавання на льоту
Джордж Мауер

Було б важливо, якби «нарізка та нарізка» грунтувалася на спостережуваних даних. Наприклад, припустимо, що нарізання та нарізання - це вузли, які мають .IsChecked () == true. Тоді у вас буде обчислюваний файл .currentValues ​​(), який оцінить усі вузли і поверне ті з .IsChecked (). Чи можна нарізати та нарізати кубиками за спостережуваними властивостями?
Джуда Габріель Хіманго

Я насправді не впевнений, що ти кажеш, Джуда, кожен окремий виклик не може бути ко-спостережуваним у моєму випадку - але, що більш важливо, я не розумію, як це може мати значення, нокаут не робить жодних роздумів які закриття ви викликали (я не перевіряв, але якщо javascript повністю не перейшов у lisp-land, це неможливо). Все, що обчислюване може зробити, це в кращому випадку відстежувати будь-які спостережувані, які викликаються.
Джордж Мауер

-1

оскільки немає прямого способу примусового оновлення обчислюваного, я створив спостережуваний з назвою toForceComputedUpdate, і я викликав його в обчислюваній функції, щоб обчислюваний прослуховував його оновлення, то для примусового оновлення я називаю це як ForceComputedUpdate (Math .random)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.