knockout.js - 嵌套数组数据和级联预填充下拉列表绑定 [英] knockout.js - nested array data and cascading pre-populated dropdown lists binding

查看:327
本文介绍了knockout.js - 嵌套数组数据和级联预填充下拉列表绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对knockout.js来说相当新鲜,但是,我一直很高兴地在ASP.NET MVC 4项目中使用它,直到遇到困扰我一段时间的障碍,似乎不能把我的手指放在上面。

I'm fairly new to knockout.js, however, I've been happily using it in my ASP.NET MVC 4 project, until I ran into this obstacle which has been bothering me for a while, can't seem to put my finger on it.

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

The scenario which I'm working on requires several combinations of location data (region, country, city), i.e. cascading dropdown lists, which isn't a problem to do when inputting fresh data, but I ran into problem(s) when trying to edit the saved data.

数据采用JSON格式,嵌套数组,如下所示(缩写为说明目的):

Data is in JSON format, with nested arrays, looks like this (shortened for illustration purposes):

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

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

I suspect I can't load the data (or more clearly, to bind it) properly since I'm having trouble accessing the Region sub-array (which contains Region's Countries) and the Countries sub-array (which contains Countries' Cities).

然后有预填充选项的问题,部分工作,viewmodel加载行数,但不选择任何内容。

Then there's the matter of having prepopulated options, which works partially, the viewmodel loads the number of lines, but doesn't select anything.

这是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网站修改Cart编辑器示例,真的取得了很大的进步,我似乎在遗漏一些东西。没有找到任何嵌套数组,所以我被困在这里...

I tried to modify the Cart editor example from the knockout.js website with prepopulated data, but haven't really made much progress, I seem to be missing something. Didn't really find anything with nested arrays so I'm stuck here...

我已经在JSFiddle上填写了完整的代码:
< a href =http://jsfiddle.net/fgXA2/1/ =nofollow> http://jsfiddle.net/fgXA2/1/

I've put up the full code on JSFiddle here: http://jsfiddle.net/fgXA2/1/

任何帮助将不胜感激。

推荐答案

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

The problem is the way in which you are binding to the selected item in your select lists:

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

这里您绑定了 ID 您的JSON数据到视图模型上的区域属性。

Here you are binding the ID property from your JSON data to the region property on your view model.

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

This means that when you bind your second select list:

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

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

You attempt to bind to region.Countries. However, region simply contains the selected region ID. In this case the console is your friend:


未捕获错误:无法解析绑定。消息:ReferenceError:
国家未定义;

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

同样的问题也是您自现在试图绑定到 country.Cities 其中 country 也只是 ID

The same problem is true of your third select list for Cities since you are now attempting to bind to country.Cities where country is also just the ID.

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

There are two options available here. The first is to remove the optionsValue parameters, thus binding the actual JSON objects to your view model properties. That and a binding error on your Cities select box (you were binding to CityName instead of Name) were the only problems:

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

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

As you can see from the example I've used the ko.toJSON utility to output your view model's object graph. This can be very useful in resolving problems (in your case you would have seen that the region property was just an number).

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

The downside of the above approach is that you end up storing a copy of all of the countries, and their cities for the selected country in your view model.

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

A better solution if dealing with large data sets would be to only store the selected identifier (which I believe you were attempting to do originally) and then define computed properties that filter your single data set for the required values.

这个例子可以在 http://jsfiddle.net/benfosterdev / Bbbt3 ,使用以下计算属性:

An example of this can be seen at http://jsfiddle.net/benfosterdev/Bbbt3, using the following computed properties:

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

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

You can see from the rendered object graph that only the currently selected countries and cities are copied to the view model.

这篇关于knockout.js - 嵌套数组数据和级联预填充下拉列表绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆