2013-01-23 80 views
4

我對knockout.js相當陌生,但是,我一直很高興地在ASP.NET MVC 4項目中使用它,直到遇到這個困擾我一段時間的障礙似乎無法指引我。knockout.js - 嵌套數組數據和級聯預填充下拉列表綁定

我正在處理的場景需要位置數據(地區,國家,城市)的幾個組合,即級聯下拉列表,這不是輸入新數據時要做的問題,但我遇到了問題( s)時試圖編輯保存的數據。

數據處於JSON格式,具有嵌套的數組,看起來像這樣(縮短用於說明目的):

var newData = 
[ 
    { 
    "ID":1, 
    "Name":"Australia and New Zealand", 
    "Countries":[ 
     { 
     "ID":13, 
     "Name":"Australia", 
     "Cities":[ 
      { 
      "ID":19, 
      "Name":"Brisbane" 
      }, 
      { 
      "ID":28, 
      "Name":"Cairns" 
      }, 
... 

我懷疑無法加載的數據(或者更明確地,爲了綁定它),因爲我在訪問區域子數組(包含區域國家)和國家子數組(包含國家城市)時遇到問題。

然後有預填充選項的問題,它部分工作,viewmodel加載行數,但不選擇任何東西。

這裏的VM:

var existingRows = [ 
    { 
     "Region": 1, 
     "Country": 13, 
     "City": 19 
    }, 
    { 
     "Region": 1, 
     "Country": 158, 
     "City": 3 
    }]; 

    var Location = function (region, country, city) { 
     var self = this; 
     self.region = ko.observable(region); 
     self.country = ko.observable(country); 
     self.city = ko.observable(city); 

     // Whenever the region changes, reset the country selection 
     self.region.subscribe(function() { 
      self.country(undefined); 
     }); 

     // Whenever the country changes, reset the city selection 
     self.country.subscribe(function() { 
      self.city(undefined); 
     }); 
    }; 

    var LocationViewModel = function (data) { 
     var self = this; 

     self.lines = ko.observableArray(ko.utils.arrayMap(data, function (row) 
     { 
      var rowRegion = ko.utils.arrayFirst(newData, function (region) 
      { 
       return region.ID == row.Region; 
      }); 
      var rowCountry = ko.utils.arrayFirst(rowRegion.Countries, function (country) { 
       return country.ID == row.Country; 
      }); 
      var rowCity = ko.utils.arrayFirst(rowCountry.Cities, function (city) { 
      return city.ID == row.City; 
      }); 
      return new Location(rowRegion, rowCountry, rowCity); 
     })); 

     // Operations 
     self.addLine = function() { 
      self.lines.push(new Location()) 
     }; 
     self.removeLine = function (line) { 
      self.lines.remove(line) 
     }; 
    }; 

    var lvm = new LocationViewModel(existingRows); 

    $(function() { 
     ko.applyBindings(lvm); 
    }); 

HTML代碼:

<tbody data-bind="foreach: lines"> 
    <tr> 
     <td><select data-bind="options: newData, optionsText: 'Name', optionsValue: 'ID', optionsCaption: 'Select a region...', attr: { name: 'SubRegionIndex' + '['+$index()+']' }, value: region"></select></td> 
     <td><select data-bind="options: Countries, optionsText: 'Name', optionsValue: 'ID', optionsCaption: 'Select a country...', attr: { name: 'CountryIndex' + '['+$index()+']' }, value: country"></select></td> 
     <td><select data-bind="options: Cities, optionsText: 'Name', optionsValue: 'ID', optionsCaption: 'Select a city...', attr: { name: 'CityIndex' + '['+$index()+']' }, value: city"></select></td> 
     <td><a href='#' data-bind='click: $parent.removeLine'>Remove</a></td> 
    </tr>  
</tbody> 

我試圖從knockout.js網站與預填充的數據修改車編輯器的例子,但還沒有真正取得多大進步,我似乎失去了一些東西。沒有真正與嵌套數組發現任何東西,所以我堅持在這裏......

我已經忍了完整的代碼上的jsfiddle這裏: http://jsfiddle.net/fgXA2/1/

任何幫助,將不勝感激。

回答

5

的問題是,你在你的選擇列表結合所選項目的方式:

<select data-bind=" 
    options: newData, 
    optionsText: 'Name', 
    optionsValue: 'ID', 
    value: region"> 
</select> 

在這裏,你是從你的JSON數據ID屬性綁定到你的視圖模型的region財產。

這意味着,當你綁定你的第二個選擇列表:

<td data-bind="with: region"> 
    <select data-bind=" 
     options: Countries, 
     optionsText: 'Name', 
     optionsValue: 'ID', 
     value: $parent.country"> 
    </select> 
</td> 

您嘗試綁定到region.Countries。但是,region只包含選定區域ID。在這種情況下,控制檯是你的朋友:

Uncaught Error: Unable to parse bindings. Message: ReferenceError: Countries is not defined;

同樣的問題是你的第三個選擇列表城市真正的,因爲你現在試圖綁定到country.Cities其中country還僅僅只是ID

這裏有兩個選項。首先是刪除optionsValue參數,從而將實際的JSON對象綁定到視圖模型屬性。在你的城市,在和綁定錯誤選擇框(你被綁定到CityName代替Name)是唯一的問題:

http://jsfiddle.net/benfosterdev/wHtRZ/

正如你看到的例子,我用了ko.toJSON實用程序輸出你的視圖模型的對象圖。這可能是解決問題(在你的情況,你會看到,region屬性只是一個數字)是非常有用的。

上述辦法的缺點是,你最終存儲所有國家的副本,以及他們在您的視圖模型所選國家的城市。

一個更好的解決方案,如果處理大型數據集將只存儲選擇的標識符(我相信你試圖做最初),然後定義篩選單個數據爲需要的值設定計算性能。

這樣的一個例子可以在http://jsfiddle.net/benfosterdev/Bbbt3可以看出,使用以下計算的屬性:

var getById = function (items, id) { 
     return ko.utils.arrayFirst(items, function (item) { 
      return item.ID === id; 
     }); 
    }; 

    this.countries = ko.computed(function() { 
     var region = getById(this.selectedRegion.regions, this.selectedRegion()); 
     return region ? ko.utils.arrayMap(region.Countries, function (item) { 
      return { 
       ID: item.ID, 
       Name: item.Name 
      }; 
     }) : []; 
    }, this); 

    this.cities = ko.computed(function() { 
     var region = getById(this.selectedRegion.regions, this.selectedRegion()); 
     if (region) { 
      var country = getById(region.Countries, this.selectedCountry()); 
      if (country) { 
       return country.Cities; 
      } 
     } 

    }, this); 

可以從只有當前選擇的國家和城市被複制到視圖模型的渲染對象圖看到。

+0

謝謝你的解決方案,並且調試指針!真的很感激它。只是一個後續的問題 - 因爲我提交表單到服務器,我仗着optionsValue,所以我想我將不得不使用你的第二個建議,可用最好的選擇? – zpodbojec

+0

回答我自己的問題 - 我已經按照您的第二個建議,並使用添加/刪除行選項對其進行了擴展。很棒。謝謝你的靈感:) – zpodbojec

+0

如果您使用的是正常的表單提交發送數據到服務器,然後是你需要設置'optionsValue'參數。或者,您可以使用視圖模型來提交數據,在這種情況下,您可以完全控制發送的內容。 –