Knockout.js 与多个 getJSON 请求绑定

Knockout.js binding with multiple getJSON requests

我的示例工作正常,但我对 ko.applyBindings() 语句的位置感到困惑。我使用 this approach 从单个 getJSON 请求填充我的 ViewModel。但是假设我需要 2 个 getJSON 请求。我将 "var viewModel = new MyViewModel();" 移到 getJSON 之外,但是 ko.applyBinding() 在两个 getJSON 方法中,我知道你不应该对同一个方法有 2 个绑定虚拟机。我尝试将 ko.applyBinding() 移动到 getJSON 下方,但没有任何效果。所以我将 ko.applyBinding() 留在其中一个 getJSON 方法中,并调用 VM 方法来设置来自另一个 JSON 调用的变量。它似乎有效,但我担心如果 JSON 在不同时间请求 return 是否存在时间问题可能会导致问题。

var MyViewModel = function() {
    var self = this;
    self.types = ko.observableArray();
    self.states = ko.observableArray();
    self.loadStates = function (states){
        self.states = states;
    }
}
var viewModel = new MyViewModel();
$(function () {
    $.getJSON('json/typeArray.json', function(jTypes){
        viewModel.types = jTypes;
        ko.applyBindings(viewModel);
    });
    $.getJSON('json/stateArray.json', function(jStates){
        viewModel.loadStates(jStates);
        //ko.applyBindings(viewModel);
    });
});

我可以使用嵌套的 JSON 请求,但我希望它们同时执行。

为什么不能将 ko.applyBindings(viewModel) 移动到此脚本的底部?我试过了,但我的两个数组都没有填充。

Update: Yes, there is a timing problem. Sometimes the 2nd "states" array gets updated in the UI, and sometimes it does not. It evidently depends on which getJSON returns first. So I do need to find a solution to this problem.

这是在创建 viewModel 后移动 applyBindings 的尝试,但没有成功(见评论):

var MyViewModel = function() {
    var self = this;
    self.name = "myViewModel";
    self.states = ko.observableArray();
    self.types = ko.observableArray();
    self.loadStates = function (states){
        self.states = states;
        console.log("Set states in viewModel: " + self.states);
    }
}

var viewModel = new MyViewModel();
ko.applyBindings(viewModel);

$(function () {
    $.getJSON('json/typeArray.json', function(jTypes){
        console.log("Setting types in viewModel: " + viewModel.name);
        viewModel.types = jTypes;
        //ko.applyBindings(viewModel);
    });
    $.getJSON('json/stateArray.json', function(jStates){
        console.log("Setting states in viewModel: " + viewModel.name);
        viewModel.loadStates(jStates);
        //ko.applyBindings(viewModel);
    });
});

请注意,在这个回答中,我明确地重新发明了轮子,以展示其工作原理的基本概念。有一些库,包括您似乎已经在使用的库 (jQuery),它们使这项任务变得更加容易和流畅。

基本上,您想要 运行 ko.applyBindings,但只有在完成两个单独的异步请求之后。这是执行此操作的模式:

var viewModel = new MyViewModel();

function tryApplyBindings() {
  if (viewModel.types().length > 0 &&
      viewModel.states().length > 0) {
    ko.applyBindings(viewModel);
  }
}

$(function () {
    $.getJSON('json/typeArray.json', function(jTypes){
        console.log("Setting types in viewModel: " + viewModel.name);
        viewModel.types = jTypes;
        tryApplyBindings();
    });
    $.getJSON('json/stateArray.json', function(jStates){
        console.log("Setting states in viewModel: " + viewModel.name);
        viewModel.loadStates(jStates);
        tryApplyBindings();
    });
});

但是,请再次注意:这是重新发明轮子。您可以使用 jQuery 的 promise 功能(例如 this approach,也许)来制作更优雅和 DRY 的 promise 组合。


作为脚注,您也可以直接考虑 运行ning applyBindings,并创建一个看起来也很适合空数组的视图。然后,当承诺 return 时,UI 会自动更新。

问题是您为可观察数组设置了新值,而不是将新对象分配给绑定属性。

而不是赋值:

viewModel.types = jTypes;

我建议使用更新:

//viewModel.types(jTypes);
viewModel.types(["type a", 'type b', 'type c']);

我创建了一个示例(通过 setTimeout 模拟请求),启动时数组为空,"times" 在 1 秒内更新,"states" 在 2 秒内更新:

var MyViewModel = function() {
    var self = this;
    self.name = "myViewModel";
    self.states = ko.observableArray();
    self.types = ko.observableArray();
}

var viewModel = new MyViewModel();
ko.applyBindings(viewModel);

//$(function () {
//    $.getJSON('json/typeArray.json', function(jTypes){
//        viewModel.types(jTypes);
//    });
//    $.getJSON('json/stateArray.json', function(jStates){
//        viewModel.states(jStates);
//    });
//});

//$.getJSON('json/typeArray.json', function(jTypes){
setTimeout(function() {
    viewModel.types(["type a", 'type b', 'type c'])
}, 1000);

//$.getJSON('json/stateArray.json', function(jStates){
setTimeout(function() {
    viewModel.states(["state d", 'state e', 'state f'])
}, 2000);

// ever more - update types again in 5 sec
//$.getJSON('json/typeArray.json', function(jTypes){
setTimeout(function() {
    viewModel.types(["type g", 'type h', 'type i', 'type j'])
}, 5000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div>States:</div>
<!-- ko if: states().length === 0 -->
<div>There are no states for a while...</div>
<!-- /ko -->
<!-- ko foreach: states -->
<div data-bind="text: $data"></div>
<!-- /ko -->
  
<div>Types:</div>
<!-- ko if: types().length === 0 -->
<div>There are no types for a while...</div>
<!-- /ko -->
<!-- ko foreach: types -->
<div data-bind="text: $data"></div>
<!-- /ko -->