我对knockout.js很新,不过,我一直很乐意在我的ASP.NET MVC 4项目中使用它,直到我遇到这个困扰我一段时间的障碍,似乎无法忍受把手指放在上面。
我正在处理的场景需要几种位置数据组合(区域,国家,城市),即级联下拉列表,这在输入新数据时不是问题,但我遇到了问题(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"
},
...
我怀疑我无法正确加载数据(或更清楚地说,绑定),因为我无法访问Region子阵列(包含Region的国家/地区)和国家/地区子阵列(包含国家的城市)。
然后是预先填充选项的问题,这部分工作,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 code:
<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/
任何帮助都将不胜感激。
答案 0 :(得分: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
。在这种情况下,控制台是你的朋友:
未捕获错误:无法解析绑定。消息:ReferenceError: 国家没有定义;
同样的问题适用于您的第三个城市选择列表,因为您现在正尝试绑定到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);
您可以从渲染对象图中看到,只有当前选定的国家/地区和城市被复制到视图模型。