Knockout:选择元素选项绑定+值绑定+ optionsCaption绑定+后期ajax响应=怪异

时间:2012-10-16 12:14:50

标签: asp.net-mvc asp.net-mvc-3 knockout.js asp.net-mvc-4 knockout-2.0

考虑select元素上的以下ko绑定:

<select data-bind="value: valueObservable, options: optionsObservableArray, 
    optionsCaption: '[None] - this is an optional field'">

...给出如下的viewmodel:

function MyViewModel()
{
    var self = this;
    self.valueObservable = ko.observable();
    self.optionsObservableArray = ko.observableArray();

    // ajax call to load options
    ko.computed(function() {
        $.ajax(...)
        .success(function(optionsResponse) {
            self.optionsObservableArray(optionsResponse)
        });
    });

    // ajax call to load data value
    ko.computed(function() {
        $.ajax(...)
        .success(function(valueResponse) {
            self.valueObservable(valueResponse)
        });
    });
}

这有什么奇怪的,当第二个(数据)ajax调用在第一个(选项)ajax调用之前返回时。由于select元素具有optionsCaption绑定,因此我认为这是正在发生的事情:

  1. 数据ajax调用完成,将valueObservable设置为某个值(如6,abc或其他一些非虚假值)。
  2. 因为option元素中只有select(由于optionsCaption),并且因为valueObservable绑定了它(通过{{1 }}绑定),这会导致value更改为valueObservable
  3. 最后,undefined完成并向optionsObservableArray添加新的option元素,但此时为时已晚:select现在正在包裹valueObservable 1}}值而不是第一个ajax调用返回的实际数据值。
  4. 问题:解决此问题的最佳方法是什么?这是我能想到的:

    1. 使用undefined进行第一次ajax调用。这可能会降低页面渲染速度。
    2. 创建一个单独的observable以绑定到选择值(例如async: false)。然后订阅value: selectedValueObservable并使用订阅执行optionsObservableArray之类的操作。这似乎是一个绑带修复。
    3. 渲染选择&amp;通过从服务器发送选项数据(使用MVC viewmodel)执行任何javascript之前页面的选项。这使得以self.selectedValueObservable(self.valueObservable())
    4. 处理选项变得更加困难

      更新

      为了简化示例代码,我在此问题中省略了另一个问题。实际上,此视图模型用于创建可编辑的数据项列表。因此实际上有超过1个下拉列表被呈现到页面。数据ajax调用确实返回一个数组,其成功函数实际上设置了observableArray个复杂项。由于下拉列表选项在每个内联表单中都被重用,因此它被放置在observableArray视图模型上,并且只加载一次。在单个ajax调用中传递选项也很困难,因为数据项是通过WebAPI检索的(返回$parent,没有空间发送额外的下拉选项)。

2 个答案:

答案 0 :(得分:1)

如果有可能的话,我建议你进行一次ajax调用。使控制器返回包含对象数组和选定值的复杂对象:

// ajax call to load options and data value
ko.computed(function() {
    $.ajax(...)
    .success(function(response) {
        self.optionsObservableArray(response.options);
        self.valueObservable(response.value);
    });
});

如果无法合并两个ajax调用,你可以调用第二个ajax来成功回调第一个ajax:

// ajax call to load options
ko.computed(function() {
    $.ajax(...)
    .success(function(optionsResponse) {
        self.optionsObservableArray(optionsResponse)

        // ajax call to load data value
        $.ajax(...)
        .success(function(valueResponse) {
             self.valueObservable(valueResponse)
        });
    });
});

答案 1 :(得分:1)

有没有理由不首先进行ajax调用,并且在完成后使用applyBinding?

$.when(getOptions(), getData()).done(bind) 


function getOptions() { 
    return $.ajax(...).success(vm.optionsObservableArray)
}

function getData() {
    return $.ajax(...).success(vm.valueObservable)
}

function bind() {
    ko.applyBindings(vm, document.getElementById('elem')) 
}