计算出的"睡眠"直到一些初始化完成

时间:2015-11-03 08:48:15

标签: javascript knockout.js

我有一个复杂的viewmodel,它包含几个可观察的属性,数组等。我有一个计算的observable,它有几个依赖项。计算出的内部逻辑必须在定义时执行,但只能在模型完全初始化后执行。

示例:

用户可以选择更多大洲,国家或城市之一的表单。每次选择一些大陆时,国家列表应仅包含位于选定大洲的国家/地区,城市等也应如此。

为优化HTTP流量,初始列表将作为页面的一部分填充,因此不需要初始JSON请求。

var viewModel = function(data) {
  this.Continents = ko.observableArray(data.Continents);
  this.Countries = ko.observableArray(data.Countries);
  this.Cities = ko.observableArray(data.Cities);

  this.SelectedContinents = ko.observableArray(data.SelectedContinents);
  this.SelectedCountries = ko.observableArray(data.SelectedCountries);
  this.SelectedCities = ko.observableArray(data.SelectedCities);

  this.LoadFromServer = function() {
    $.post({
      url: '/reloadLists',
      data: { continents: this.SelectedContinents(), countries: this.SelectedCountries(), cities: this.SelectedCities() },
      success: function(result) {
        this.Continents(result.Continents);     
        this.Countries(result.Countries);
        this.Cities(result.Cities);
      } 
    });
  };

  ko.computed(function() {
    this.LoadFromServer();
  }, this);

}

...

var data = ... // initial data rendered on server-side 
var model = new viewModel(data);
ko.applyBindings(model);

使用这种方法,问题是当计算机最初执行rad函数时,LoadFromServer逻辑也会在模型初始化时执行。这种方式有一个冗余的往返,因为初始列表已经在模型中。

我现在能想到的唯一解决方案是引入一个标志来阻止具体逻辑,直到需要它为止。这个标志不应该是一个可观察的,因为当我在构造函数的末尾将它设置为true时,计算器将被重新评估,并且冗余的往返再次进行。但是,如果该标志不是可观察的,那么我必须确保在初始化时捕获依赖项,以便它可以在之后对更改做出反应。把所有这些放在一起,电流结果就像这样。

var viewModel = function(data) {

  var initialized = false;

  this.Continents = ko.observableArray(data.Continents);
  this.Countries = ko.observableArray(data.Countries);
  this.Cities = ko.observableArray(data.Cities);

  this.SelectedContinents = ko.observableArray(data.SelectedContinents);
  this.SelectedCountries = ko.observableArray(data.SelectedCountries);
  this.SelectedCities = ko.observableArray(data.SelectedCities);

  this.LoadFromServer = function() {
    $.post({
      url: '/reloadLists',
      data: { continents: this.SelectedContinents(), countries: this.SelectedCountries(), cities: this.SelectedCities() },
      success: function(result) {
        this.Continents(result.Continents);     
        this.Countries(result.Countries);
        this.Cities(result.Cities);
      } 
    });
  };

  ko.computed(function() {
    var catchDependencies = [this.SelectedContinents(), this.SelectedCountries(), this.SelectedCities()];
    if (!initialized) return;
    this.LoadFromServer();
  }, this);  

  initialized = true;
}

这在技术上是一个很好的解决方案,但我不太喜欢它,因为它有一些气味。

这些方案有没有更好的解决方案?或者我不应该尝试优化事物并让初始AJAX加载而不是服务器端初始数据呈现?

4 个答案:

答案 0 :(得分:2)

尝试

ko.computed(function() {
  if(ko.computedContext.isInitial()) return;
  this.LoadFromServer();
}, this);

了解更多Here

答案 1 :(得分:2)

对我而言,一个不返回值的计算器是代码气味,因为它是一个有一个目的的工具,你正在使用它作为一种多订阅。设置显式订阅会更清楚:

  this.SelectedContinents.subscribe(this.LoadFromServer);
  this.SelectedCountries.subscribe(this.LoadFromServer);
  this.SelectedCities.subscribe(this.LoadFromServer);

我不清楚LoadFromServer是否需要外部可见;你可以把它作为私人功能。

答案 2 :(得分:0)

嗨,我不确定这是不是最好的方法,但它有效。

所以我的方法是,我创建了一个名为isComplete的标志,如果这是真的,则显示计算值,否则显示默认值。这适用于HTML,因此绑定不会失败。

此外,我添加了一个按钮,我在其中注册计算值。它也可以订阅某些价值。



var ViewModel = function(first, last) {
    var self = this;
    self.firstName = ko.observable(first);
    self.lastName = ko.observable(last);
 	self.isComplete = ko.observable(false);
  
    // Computed registered section
    self.registerComputed = function(){
        if(!self.hasOwnProperty("fullName")){
            self.fullName = ko.computed(function() {
                return this.firstName() + " " + this.lastName();
            }, this);
        }
        self.isComplete(true);
    }
};
 
ko.applyBindings(new ViewModel("Planet", "Earth"));

body { font-family: arial; font-size: 14px; }
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
.liveExample select[multiple] { width: 100%; height: 8em; }
.liveExample h2 { margin-top: 0.4em; font-weight: bold; font-size: 1.2em; }

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class='liveExample'>   
    <p>First name: <input data-bind='value: firstName' /></p> 
    <p>Last name: <input data-bind='value: lastName' /></p> 
    <h2>Hello, <span data-bind='text: isComplete()?fullName():""'> </span>!</h2>  
    <button data-bind="click:registerComputed, text:'Register Full Name'"></button>
</div>
&#13;
&#13;
&#13;

答案 3 :(得分:0)

您需要使用的是computed observable deferEvaluation选项:

  

可选。如果此选项为true,则在实际尝试访问其值或手动订阅它之前,不会计算计算的observable的值。默认情况下,计算的observable在创建过程中会立即确定其值。

我的想法是设置此选项,并在初始化完成后手动访问observable,以使其“变为活动状态”。当然,您必须将其存储在“私有”变量中才能访问它。

请同时参阅:Lazy Loading an Observable in KnockoutJS

相关问题