淘汰赛,当我尝试从其中删除一项时,集合 属性 变为空,如果其中有多个项目?
Knockout, collection property becomes null when I try and remove one item from it if it has more than one item in it?
我遇到了非常奇怪的敲除错误。这是一个相当复杂的场景,所以请看这个 fiddle:
基本上我有一个双层嵌套集合,第一个集合 FishMeasurements
包含一个对象集合,该集合具有与之相关的物种信息,以及一个 Measurements
集合,其中包含与相关的所有测量值那个物种。
现在,当我尝试从 HTML 的嵌套集合中删除项目时:
<!-- ko foreach: FishMeasurements() -->
<h3><span data-bind="text: SpeciesName"></span><span data-bind="text: SpeciesId" style="display: none;"></span></h3>
<table class="table table-striped">
<thead>
<tr>
<th>Length</th>
<th>Count</th>
<th>Weight</th>
<th>Fish Id</th>
<th> </th>
</tr>
</thead>
<tbody data-bind="foreach: Measurements()">
<tr>
<td><span data-bind="text: LengthInMillimeters"></span></td>
<td><span data-bind="text: Count"></span></td>
<td><span data-bind="text: WeightInPounds"></span></td>
<td><span data-bind="text: FishCode"></span></td>
<td><a href="#" data-bind="click: function() { $root.removeMeasurement($data, $index); }">Remove</a></td>
</tr>
</tbody>
</table>
<!-- /ko -->
当 Measurements
集合有多个对象时,删除测量功能不起作用。我单击删除 link,它会抛出一条错误消息:
VM617:163 Uncaught TypeError: Cannot read property 'Measurements' of null(…)
奇怪的是,如果我只将一个项目添加到测量集合,删除按钮可以正常工作,但是一旦我添加多个测量,如果我在 [=40] 中的任何项目上单击删除=] 但是第一行,就产生了这个错误。但是,如果我单击每个物种 table 中的第一项,则没有错误并且所有记录都被删除!
有些事情告诉我它把 Measurements 当作一个对象而不是一个集合来处理,因为它只适用于索引 0。但我不确定,因为在控制台中我可以输入:
mappedModel.FishMeasurements()[0].Measurements()[1]
并返回一个完整的 ko_mapping 对象,因此它不是空的。但是由于某种原因,当我单击删除时,它为空。只要每个物种只有一个测量值,单击删除就可以正常工作,一旦有更多它就会中断。
我做错了什么?
当您 addMeasurement
第一次 speciesId
时,speciesName
正在被定义,因为 fishMeasurementBySpecies === undefined
因此当您删除第一个项目时,您有一个有效的 measurement.SpeciesId()
作为 removeMeasurement
函数内部的参数,但这是第二次而且更多,因为 fishMeasurementBySpecies
不再是 undefined
然后 speciesId
、speciesName
永远不会设置并且那么当调用removeMeasurement
时,measurement.SpeciesId()
为空。
为了使您的模型正常工作,您需要应用以下更改。
在您的 if 语句
之前定义 var speciesId = mappedModel.SelectedSpecies();
var speciesId = mappedModel.SelectedSpecies();
if (fishMeasurementBySpecies === undefined || fishMeasurementBySpecies === null) {
将 ()
for Measurements
放入您想要获取长度的 removeMeasurement 函数中
if(fishMeasurementBySpecies.Measurements().length === 0)
下面我为您提供了一个示例,说明您想通过使用手动视图模型而不是使用 mapping
插件来做什么。
示例:https://jsfiddle.net/kyr6w2x3/118/
你的例子:http://jsfiddle.net/yx8dkLnc/1/
虚拟机:
var data = {
"AvailableSpecies":
[
{"Id":"f57830b8-0766-4374-b481-82c04087415e","Name":"Alabama Shad"},
{"Id":"3787ce10-e61c-4f03-88a5-ff648bb55480","Name":"Alewife"},{"Id":"e923214f-4974-4663-9158-d6979ce637f1","Name":"All Sunfish Spp Ex Bass And Crappie"} ],
"SelectedSpecies": null, "CountToAdd":0,"LengthToAdd":0,"WeightToAdd":0,"GenerateFishCode":false,"FishMeasurements":[]
};
function AppViewModel(){
var self = this;
self.AvailableSpecies = ko.observableArray(data.AvailableSpecies);
self.SelectedSpecies = ko.observable();
self.CountToAdd = ko.observable();
self.LengthToAdd = ko.observable();
self.WeightToAdd = ko.observable();
self.FishCode = ko.observable();
self.FishMeasurements = ko.observableArray([]);
self.addMeasurement = function(item) {
var SpeciesExists = false;
ko.utils.arrayForEach(self.FishMeasurements(), function (item) {
if(item.SpeciesId() == self.SelectedSpecies().Id) {
var len = item.Measurements().length;
// you may have a better way to generate a unique Id if an item is removed
while(item.Measurements().findIndex(x => x.Id() === len) > 0){
len++;
}
item.Measurements.push(new MeasurementsViewModel({LengthInMillimeters:self.LengthToAdd(),
Count:self.CountToAdd(),
WeightInPounds:self.WeightToAdd(),
FishCode:self.FishCode(),
Id:len ++,
ParentId:self.SelectedSpecies().Id
})
);
SpeciesExists = true;
}
});
if(!SpeciesExists){
self.FishMeasurements.push(new FishMeasurementsViewModel({SpeciesName:self.SelectedSpecies().Name,
SpeciesId:self.SelectedSpecies().Id,
Measurements:[{LengthInMillimeters:self.LengthToAdd(),
Count:self.CountToAdd(),
WeightInPounds:self.WeightToAdd(),
FishCode:self.FishCode(),
Id:1}]
})
);
}
}
self.removeMeasurement = function(data){
ko.utils.arrayForEach(self.FishMeasurements(), function (item) {
if(item && item.SpeciesId() == data.ParentId()) {
ko.utils.arrayForEach(item.Measurements(), function (subItem) {
if(subItem && subItem.Id() == data.Id()) {
item.Measurements.remove(subItem);
}
});
}
if(item && item.Measurements().length == 0){
self.FishMeasurements.remove(item);
}
});
}
}
var FishMeasurementsViewModel = function(data){
var self = this;
self.SpeciesName = ko.observable(data.SpeciesName);
self.SpeciesId = ko.observable(data.SpeciesId);
self.Measurements = ko.observableArray($.map(data.Measurements, function (item) {
return new MeasurementsViewModel(item,self.SpeciesId());
}));
}
var MeasurementsViewModel = function(data,parentId){
var self = this;
self.LengthInMillimeters = ko.observable(data.LengthInMillimeters);
self.Count = ko.observable(data.Count);
self.WeightInPounds = ko.observable(data.WeightInPounds);
self.FishCode = ko.observable(data.FishCode);
self.Id = ko.observable(data.Id);
self.ParentId = ko.observable(parentId ? parentId : data.ParentId);
}
var viewModel = new AppViewModel();
ko.applyBindings(viewModel);
我遇到了非常奇怪的敲除错误。这是一个相当复杂的场景,所以请看这个 fiddle:
基本上我有一个双层嵌套集合,第一个集合 FishMeasurements
包含一个对象集合,该集合具有与之相关的物种信息,以及一个 Measurements
集合,其中包含与相关的所有测量值那个物种。
现在,当我尝试从 HTML 的嵌套集合中删除项目时:
<!-- ko foreach: FishMeasurements() -->
<h3><span data-bind="text: SpeciesName"></span><span data-bind="text: SpeciesId" style="display: none;"></span></h3>
<table class="table table-striped">
<thead>
<tr>
<th>Length</th>
<th>Count</th>
<th>Weight</th>
<th>Fish Id</th>
<th> </th>
</tr>
</thead>
<tbody data-bind="foreach: Measurements()">
<tr>
<td><span data-bind="text: LengthInMillimeters"></span></td>
<td><span data-bind="text: Count"></span></td>
<td><span data-bind="text: WeightInPounds"></span></td>
<td><span data-bind="text: FishCode"></span></td>
<td><a href="#" data-bind="click: function() { $root.removeMeasurement($data, $index); }">Remove</a></td>
</tr>
</tbody>
</table>
<!-- /ko -->
当 Measurements
集合有多个对象时,删除测量功能不起作用。我单击删除 link,它会抛出一条错误消息:
VM617:163 Uncaught TypeError: Cannot read property 'Measurements' of null(…)
奇怪的是,如果我只将一个项目添加到测量集合,删除按钮可以正常工作,但是一旦我添加多个测量,如果我在 [=40] 中的任何项目上单击删除=] 但是第一行,就产生了这个错误。但是,如果我单击每个物种 table 中的第一项,则没有错误并且所有记录都被删除!
有些事情告诉我它把 Measurements 当作一个对象而不是一个集合来处理,因为它只适用于索引 0。但我不确定,因为在控制台中我可以输入:
mappedModel.FishMeasurements()[0].Measurements()[1]
并返回一个完整的 ko_mapping 对象,因此它不是空的。但是由于某种原因,当我单击删除时,它为空。只要每个物种只有一个测量值,单击删除就可以正常工作,一旦有更多它就会中断。
我做错了什么?
当您 addMeasurement
第一次 speciesId
时,speciesName
正在被定义,因为 fishMeasurementBySpecies === undefined
因此当您删除第一个项目时,您有一个有效的 measurement.SpeciesId()
作为 removeMeasurement
函数内部的参数,但这是第二次而且更多,因为 fishMeasurementBySpecies
不再是 undefined
然后 speciesId
、speciesName
永远不会设置并且那么当调用removeMeasurement
时,measurement.SpeciesId()
为空。
为了使您的模型正常工作,您需要应用以下更改。
在您的 if 语句
之前定义var speciesId = mappedModel.SelectedSpecies();
var speciesId = mappedModel.SelectedSpecies();
if (fishMeasurementBySpecies === undefined || fishMeasurementBySpecies === null) {
将 ()
for Measurements
放入您想要获取长度的 removeMeasurement 函数中
if(fishMeasurementBySpecies.Measurements().length === 0)
下面我为您提供了一个示例,说明您想通过使用手动视图模型而不是使用 mapping
插件来做什么。
示例:https://jsfiddle.net/kyr6w2x3/118/
你的例子:http://jsfiddle.net/yx8dkLnc/1/
虚拟机:
var data = {
"AvailableSpecies":
[
{"Id":"f57830b8-0766-4374-b481-82c04087415e","Name":"Alabama Shad"},
{"Id":"3787ce10-e61c-4f03-88a5-ff648bb55480","Name":"Alewife"},{"Id":"e923214f-4974-4663-9158-d6979ce637f1","Name":"All Sunfish Spp Ex Bass And Crappie"} ],
"SelectedSpecies": null, "CountToAdd":0,"LengthToAdd":0,"WeightToAdd":0,"GenerateFishCode":false,"FishMeasurements":[]
};
function AppViewModel(){
var self = this;
self.AvailableSpecies = ko.observableArray(data.AvailableSpecies);
self.SelectedSpecies = ko.observable();
self.CountToAdd = ko.observable();
self.LengthToAdd = ko.observable();
self.WeightToAdd = ko.observable();
self.FishCode = ko.observable();
self.FishMeasurements = ko.observableArray([]);
self.addMeasurement = function(item) {
var SpeciesExists = false;
ko.utils.arrayForEach(self.FishMeasurements(), function (item) {
if(item.SpeciesId() == self.SelectedSpecies().Id) {
var len = item.Measurements().length;
// you may have a better way to generate a unique Id if an item is removed
while(item.Measurements().findIndex(x => x.Id() === len) > 0){
len++;
}
item.Measurements.push(new MeasurementsViewModel({LengthInMillimeters:self.LengthToAdd(),
Count:self.CountToAdd(),
WeightInPounds:self.WeightToAdd(),
FishCode:self.FishCode(),
Id:len ++,
ParentId:self.SelectedSpecies().Id
})
);
SpeciesExists = true;
}
});
if(!SpeciesExists){
self.FishMeasurements.push(new FishMeasurementsViewModel({SpeciesName:self.SelectedSpecies().Name,
SpeciesId:self.SelectedSpecies().Id,
Measurements:[{LengthInMillimeters:self.LengthToAdd(),
Count:self.CountToAdd(),
WeightInPounds:self.WeightToAdd(),
FishCode:self.FishCode(),
Id:1}]
})
);
}
}
self.removeMeasurement = function(data){
ko.utils.arrayForEach(self.FishMeasurements(), function (item) {
if(item && item.SpeciesId() == data.ParentId()) {
ko.utils.arrayForEach(item.Measurements(), function (subItem) {
if(subItem && subItem.Id() == data.Id()) {
item.Measurements.remove(subItem);
}
});
}
if(item && item.Measurements().length == 0){
self.FishMeasurements.remove(item);
}
});
}
}
var FishMeasurementsViewModel = function(data){
var self = this;
self.SpeciesName = ko.observable(data.SpeciesName);
self.SpeciesId = ko.observable(data.SpeciesId);
self.Measurements = ko.observableArray($.map(data.Measurements, function (item) {
return new MeasurementsViewModel(item,self.SpeciesId());
}));
}
var MeasurementsViewModel = function(data,parentId){
var self = this;
self.LengthInMillimeters = ko.observable(data.LengthInMillimeters);
self.Count = ko.observable(data.Count);
self.WeightInPounds = ko.observable(data.WeightInPounds);
self.FishCode = ko.observable(data.FishCode);
self.Id = ko.observable(data.Id);
self.ParentId = ko.observable(parentId ? parentId : data.ParentId);
}
var viewModel = new AppViewModel();
ko.applyBindings(viewModel);