【问题标题】:Knockout.js binding with multiple getJSON requestsKnockout.js 绑定多个 getJSON 请求
【发布时间】:2016-07-14 07:34:00
【问题描述】:

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

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) 不能移动到这个脚本的底部?我试过了,但我的数组都没有填充。

更新:是的,存在时间问题。有时第二个“状态”数组会在 UI 中更新,有时则不会。这显然取决于首先返回哪个 getJSON。所以我确实需要找到解决这个问题的方法。

这是在创建 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);
    });
});

【问题讨论】:

  • applyBindings 应该被调用一次,例如在视图模型创建后立即。您应该在 get json done 函数中更新 observable 属性:viewModel.types(jTypes) - 我想 jTypes 是一个数组。
  • 我尝试在 viewModel 创建之后放置 applyBindings,但数组永远不会在 HTML 页面上得到更新。当我取消注释 getJSON 中的 applyBindings 时,它工作正常。请注意,console.log 条目表明 viewModel 在所有情况下都会正确更新。
  • 除了@TSV 所说的之外,您还可以使用Promise.all() 来确保在调用回调之前加载所有json:参见示例jsfiddle.net/axLokczs/2
  • 这是一个 ko 例子jsfiddle.net/axLokczs/3
  • 如果您使用我的建议(Promise.all()),您提到的种族问题就解决了

标签: knockout.js getjson


【解决方案1】:

请注意,在此答案中,我明确地重新发明了轮子,以展示其工作原理的基本概念。有一些库,包括您似乎已经在使用的库 (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 组合。


作为脚注,您还可以考虑直接运行applyBindings,并创建一个与空数组一起看起来也不错的视图。然后,当 promise 返回时,UI 会自动更新。

【讨论】:

    【解决方案2】:

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

    而不是分配:

    viewModel.types = jTypes;
    

    我建议使用更新:

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

    我创建了一个示例(通过 setTimeout 模拟请求),启动时数组为空,“时间”在 1 秒内更新,“状态”在 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 -->

    【讨论】:

    • 完美运行。异步 getJSON() 不应该导致 observableArray 出现问题——这就是我感到困惑的原因。但是我正在重置 OBJECT,而新对象 (jTypes) 是不可观察的。我需要设置当前对象的值,我只是不知道该怎么做。用 viewModel.types(jTypes) 替换 = 就可以了。当您需要等待请求完成时,“Promise”技术非常有用,但这不是这里的用例。感谢您提供正确的解决方案,以及运行代码 sn-p。
    猜你喜欢
    • 2014-02-03
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 2015-07-16
    • 1970-01-01
    • 2016-11-25
    相关资源
    最近更新 更多