1

考慮一個select元以下KO綁定:淘汰賽:選擇元素選項結合+值綁定+ optionsCaption結合+後期Ajax響應=怪異

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

...給出像下面這樣的視圖模型:

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,美國廣播公司,或其他一些非falsy值)。
  2. 由於只有1 optionselect元件(由於optionsCaption),並且因爲valueObservable被綁定到它(通過value結合),這將導致valueObservable要改變爲undefined
  3. 最後,optionsObservableArray完成,並增加了新的元素optionselect,但是在這個時候已經太晚了:在valueObservable現在包裝的undefined值,而不是從第一個AJAX調用返回的實際數據值。

問題:解決此問題的最佳方法是什麼?以下是我能想到的:

  1. 使用async: false創建第一個ajax調用。這可能會減慢頁面渲染。
  2. 爲綁定到選擇值創建單獨的observable(例如value: selectedValueObservable)。然後訂閱optionsObservableArray並使用訂閱來執行諸如self.selectedValueObservable(self.valueObservable())之類的操作。這看起來像是一個bandaid修復程序。
  3. 通過從服務器發送選項數據(使用MVC viewmodel)執行任何JavaScript執行之前,向頁面呈現選擇&選項。這使得作爲observableArray處理選項更加困難。

更新

還有另一個問題,我從這個問題省略以簡化示例代碼。實際上,此視圖模型用於創建數據項目的可編輯列表。所以實際上有超過1個下拉列表被呈現給頁面。 ajax調用的數據真的返回一個數組,並且它的成功函數確實設置了複雜項目的一個observableArray。由於下拉列表選項在每個內聯表單中都被重複使用,因此它被放置在$parent視圖模型上,並且只加載一次。在單個Ajax調用中傳遞選擇選項也很困難,因爲數據項是通過WebAPI檢索的(返回IEnumerable,沒有空間發送其他下拉選項)。

+0

將ajax調用包裝到'computed'中是什麼原因? – vittore

+0

@vittore,將ajax調用封裝在'ko.computed'中會立即執行。基本上,它通過在構建視圖模型時關閉ajax調用來初始化視圖模型。 – danludwig

+0

還有許多其他的方式可以立即執行函數,而'computed'似乎並不是爲此而設計的。 – vittore

回答

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) 
     }); 
    }); 
}); 
+0

感謝您的回答。你的第一個建議在我的情況下是不可行的,因爲所有的ajax調用都選擇了WebAPI,而不是MVC(我已經更新了這個問題來反映這個問題)。第二種選擇是可行的,但我不確定我是否喜歡將串行鏈接作爲最佳實踐。 – danludwig

1

是否有任何理由不先做ajax調用並應用綁定完成?

+0

這是一個有趣的解決方案。這比這更復雜一點,因爲vm取決於getData()函數。編輯數組中的單個項目可以更改項目出現的排序順序,因此在編輯之後,我想重新綁定整個數組(並使用ko.mapping插件執行此操作)以強制執行新的排序順序。這就是爲什麼我使用vm封裝函數來進行ajax調用,而不是將它放在vm構造和綁定代碼之外。 – danludwig

+0

你可以在你的視圖模型中執行它,但是沒有人阻止你這麼做。 「綁定」只是一個例子,因爲這兩個成功的處理程序,我只作爲例子。 – vittore

+0

我的意思是在獲得所有數據後執行綁定到DOM,因爲無論如何你都無法正確地在那一刻之前渲染DOM。 – vittore