Подтвердить что ты не робот

Есть ли способ сказать нокауту ждать пересчета вычисленных значений до тех пор, пока не будет определена модель представления?

У меня сложная модель представления, которая представляет собой пару сотен строк кода javascript с хорошим количеством наблюдаемых свойств, вычисленными наблюдаемыми свойствами, доступными для записи вычислимыми наблюдаемыми свойствами и функциями. Поэтому управлять этим довольно сложно.

Досадная проблема, с которой мне пришлось иметь дело, заключается в том, что вычисляемые наблюдаемые рассчитываются сразу же, когда вы ее определяете. Таким образом, использование переменных, которые еще не определены в модели представления в точке определения наблюдаемого, приводят к ошибкам, в которых указано, что переменная не определена. Это... только позже в файле.

Вот надуманный пример:

function ViewModel1​(args) {
    var self = this;

    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);
    self.fullName = ko.computed(function () {
        return self.firstName() + ' ' + self.lastName();
    });
}

function ViewModel2​(args) {
    var self = this;

    self.fullName = ko.computed(function () {
        // uh oh, where firstName?
        return self.firstName() + ' ' + self.lastName();
    });
    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);
}

Использование ViewModel1 будет работать без проблем. В точке fullName определено значение firstName и lastName определено так, как работает. Использование ViewModel2 не будет работать. Ошибка в вычисленной функции, указывающая firstName, не определена.

То, что я делал до сих пор, состояло в том, чтобы обеспечить, чтобы все вычисленные наблюдаемые значения были определены после определения всех зависимых переменных. Проблема заключается в том, что при этом вещи определяются в кажущихся случайными точками, когда я предпочитаю, чтобы связанные связанные переменные были определены близко друг к другу.

Одним из приятных решений, которые я придумал, является определение "инициализирующего" наблюдаемого набора на true и сделать все вычисленные тесты наблюдаемых, если он все еще инициализирует и вычисляет и возвращает значение, когда это не так. Таким образом, попытки доступа к текущей переменной undefined не будут выполнены.

function ViewModel3(args) {
    var self = this;
    var initializing = ko.observable(true);

    self.fullName = ko.computed(function () {
        if (!initializing()) {
            return self.firstName() + ' ' + self.lastName();
        }
    });
    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);

    initializing(false);
}

Но в моем случае это будет не очень практично. У меня много вычислимых наблюдаемых, поэтому делать это во всех из них будет очень раздутым, помните, что у меня их много. Дросселирование, похоже, не имеет значения.

Есть ли способ сказать нокауту подождать, прежде чем пытаться вычислить значения вычисленных наблюдаемых? Или у меня есть лучший способ структурировать мой код, чтобы справиться с этим?

Я мог бы, вероятно, сделать некоторые вспомогательные функции для управления логикой инициализации, но мне все равно пришлось бы изменять все вычисляемые наблюдаемые определения. Я полагаю, что я могу использовать нокаут для патчей, чтобы добавить эту логику инициализации, поскольку я не знаю, что нокаут имеет такие параметры, которые я мог бы просто сделать. Я раньше смотрел на источник для вычисляемых наблюдаемых данных, но не знаю, есть ли еще какой-либо параметр в другом месте.

jsfiddle demo

4b9b3361

Ответ 1

Вычислимые наблюдаемые принимают параметр deferEvaluation, который предотвращает первоначальную оценку, пока что-то действительно не попытается извлечь вычисленное значение.

Вы бы определили его как:

self.fullName = ko.computed({
   read: function() {
       return self.firstName() + " " + self.lastName();
   },
   deferEvaluation: true
});

Просто для полноты вы также можете указать его как:

this.fullName = ko.computed(function() {
       return this.firstName() + " " + this.lastName();
 }, this, { deferEvaluation: true });

Или вы можете обернуть его, как:

ko.deferredComputed = function(evaluatorOrOptions, target, options) {
   options = options || {};

   if (typeof evaluatorOrOptions == "object") {
       evaluatorOrOptions.deferEvaluation = true;   
   } else {
       options.deferEvaluation = true;
   }

   return ko.computed(evaluatorOrOptions, target, options);
};