Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/479.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript knockout.js-嵌套数组数据和级联预填充下拉列表绑定_Javascript_Data Binding_Knockout.js_Cascadingdropdown - Fatal编程技术网

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

Javascript knockout.js-嵌套数组数据和级联预填充下拉列表绑定,javascript,data-binding,knockout.js,cascadingdropdown,Javascript,Data Binding,Knockout.js,Cascadingdropdown,我对knockout.js还很陌生,但是,我一直很高兴地在我的ASP.NETMVC4项目中使用它,直到我遇到了这个困扰了我一段时间的障碍,我似乎无法找到它 我正在处理的场景需要几个位置数据组合(地区、国家、城市),即级联下拉列表,这在输入新数据时不是问题,但在尝试编辑保存的数据时遇到了问题 数据为JSON格式,带有嵌套数组,如下所示(为便于说明而缩短): 我怀疑我无法正确加载数据(或者更清楚地说,无法绑定它),因为我无法访问Region子数组(包含Region的国家)和Countries子数组(

我对knockout.js还很陌生,但是,我一直很高兴地在我的ASP.NETMVC4项目中使用它,直到我遇到了这个困扰了我一段时间的障碍,我似乎无法找到它

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

数据为JSON格式,带有嵌套数组,如下所示(为便于说明而缩短):

我怀疑我无法正确加载数据(或者更清楚地说,无法绑定它),因为我无法访问Region子数组(包含Region的国家)和Countries子数组(包含Countries的城市)

然后是预填充选项的问题,这部分起作用,viewmodel加载行数,但不选择任何内容

以下是虚拟机:

   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网站上的购物车编辑器示例,但没有取得多大进展,我似乎遗漏了一些东西。没有找到任何嵌套数组,所以我被困在这里

我在这里展示了JSFIDLE的完整代码:


任何帮助都将不胜感激。

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

<select data-bind="
    options: newData, 
    optionsText: 'Name', 
    optionsValue: 'ID', 
    value: region">
</select>
您试图绑定到
地区.国家/地区
。但是,
region
仅包含所选区域
ID
。在这种情况下,控制台是您的朋友:

未捕获错误:无法分析绑定。消息:ReferenceError: 国家没有定义

第三个城市选择列表也存在同样的问题,因为您现在正试图绑定到
国家/地区。城市
国家/地区
也只是
ID

这里有两种选择。第一个是删除
选项value
参数,从而将实际的JSON对象绑定到视图模型属性。这和城市选择框上的绑定错误(您绑定到的是
CityName
,而不是
Name
)是唯一的问题:

从示例中可以看到,我使用
ko.toJSON
实用程序输出视图模型的对象图。这在解决问题时非常有用(在您的例子中,您会看到
区域
属性只是一个数字)

上述方法的缺点是,最终会在视图模型中存储选定国家的所有国家及其城市的副本

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

使用以下计算属性,可在中查看此示例:

    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);

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

感谢您提供的解决方案和调试指针!非常感谢。只是一个后续问题-当我向服务器提交表单时,我依赖optionsValue,因此我想我必须使用您的第二个建议作为最佳选项?回答我自己的问题-我遵循了您的第二个建议,并使用添加/删除行选项对其进行了扩展。效果很好。感谢您的启发:)如果您使用普通表单提交将数据发送到服务器,那么您需要设置
选项value
参数。或者,您可以使用视图模型提交数据,在这种情况下,您可以完全控制发送的内容。
<td data-bind="with: region">
    <select data-bind="
        options: Countries, 
        optionsText: 'Name', 
        optionsValue: 'ID', 
        value: $parent.country">
    </select>
</td>
    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);