Knockout.js - 嵌套数组数据和级联预填充下拉列表绑定

2024-01-04

我对 knockout.js 相当陌生,但是,我一直很高兴在我的 ASP.NET MVC 4 项目中使用它,直到我遇到了这个困扰我一段时间的障碍,它似乎无法将我的手指放在上面。

我正在处理的场景需要位置数据的多种组合(地区、国家、城市),即级联下拉列表,这在输入新数据时不是问题,但我在尝试时遇到了问题编辑保存的数据。

数据采用 JSON 格式,带有嵌套数组,如下所示(出于说明目的而缩短):

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

我怀疑我无法加载数据(或者更清楚地说,无法加载数据)bind它)正确,因为我无法访问区域子数组(其中包含区域的国家)和国家子数组(其中包含国家的城市)。

然后是预填充选项的问题,该选项部分起作用,视图模型加载行数,但不选择任何内容。

这是虚拟机:

   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/ http://jsfiddle.net/fgXA2/1/

任何帮助,将不胜感激。


问题在于您绑定到选择列表中所选项目的方式:

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

在这里你绑定的是ID属性从您的 JSON 数据到region您的视图模型上的属性。

这意味着当您绑定第二个选择列表时:

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

您尝试绑定到region.Countries。然而,region仅包含所选区域ID。在这种情况下,控制台是你的朋友:

未捕获的错误:无法解析绑定。消息:参考错误: 国家没有定义;

您的第三个城市选择列表也存在同样的问题,因为您现在尝试绑定到country.Cities where country也只是ID.

这里有两个选项可用。第一个是删除optionsValue参数,从而绑定actualJSON 对象到您的视图模型属性。那和你的城市选择框上的绑定错误(你绑定到CityName代替Name)是唯一的问题:

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

正如您从我使用的示例中看到的ko.toJSON输出视图模型的对象图的实用程序。这对于解决问题非常有用(在您的情况下,您会发现region财产只是一个数字)。

上述方法的缺点是您最终会在视图模型中存储所有国家/地区及其城市的副本。

如果处理大型数据集,更好的解决方案是仅存储选定的标识符(我相信您最初尝试这样做),然后定义计算属性来过滤单个数据集以获取所需的值。

可以在以下位置查看此示例:http://jsfiddle.net/benfosterdev/Bbbt3 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);

从渲染的对象图中可以看到,只有当前选定的国家和城市被复制到视图模型中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Knockout.js - 嵌套数组数据和级联预填充下拉列表绑定 的相关文章

随机推荐