我有一个复杂的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加载而不是服务器端初始数据呈现?
答案 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;
答案 3 :(得分:0)
您需要使用的是computed observable deferEvaluation
选项:
可选。如果此选项为true,则在实际尝试访问其值或手动订阅它之前,不会计算计算的observable的值。默认情况下,计算的observable在创建过程中会立即确定其值。
我的想法是设置此选项,并在初始化完成后手动访问observable,以使其“变为活动状态”。当然,您必须将其存储在“私有”变量中才能访问它。