我正在学习使用MVC 4 / MVVM / Knockout进行网络管理的数据项目。在可观察数组上使用remove函数时,我遇到了更新View的问题。使用push或unshift时会发生更新,但不会删除。在chrome中使用调试器我可以看到数据正从数组中删除,更新事件无效。
html的片段如下表所示,我没有添加或编辑数据的表单。
<div id="MessageDiv" data-bind="message: Message"></div>
<div class="tableContainer hiddenHead">
<div class="headerBackground"></div>
<div class="tableContainerInner">
<table id="adapter-table" class="grid" data-bind="sortTable: true">
<thead>
<tr>
<th class="first">
<span class="th-inner">Name</span>
</th>
<th>
<span class="th-inner">DeviceID</span>
</th>
<th>
<span class="th-inner"></span>
</th>
<th>
<span class="th-inner"></span>
</th>
</tr>
</thead>
<tbody data-bind="template: { name: 'AdaptersTemplate', foreach: Adapters }">
</tbody>
</table>
<script id="AdaptersTemplate" type="text/html">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: DeviceID"></td>
<td><a href="#" data-bind="click: $parent.selectItem">Edit</a>
<td><a href="#" data-bind="click: $parent.deleteItem">Delete</a>
</tr>
</script>
</div>
<input type="button" data-bind='click: addAdapter' value="Add New Adapter" />
<input type="button" data-bind='click: saveAll' value="Save Changes" id="SaveChangesButton" />
</div>
我的javascript已经设置为将VM管理为restful并缓存更改。添加,编辑和保存/删除数据似乎都可以正常工作,而不会抛出我在Chrome中的调试器中看到的错误。确认更改似乎工作正常,并按预期对数据库进行更改。
$(function () {
var viewModel = new AdaptersModel();
getData(viewModel);
});
function getData(viewModel) {
$.getJSON("/api/AdapterList",
function (data) {
if (data && data.length > 0) {
viewModel.SetAdaptersFromJSON(data);
}
ko.applyBindings(viewModel);
});
}
//#region AdapterVM
function Adapter(name, siFamily, deviceIDs) {
var self = this;
self.Name = ko.observable(name);
self.DeviceID = ko.observable(deviceIDs);
self.ID = 0;
}
function AdaptersModel() {
var self = this;
self.Adapters = ko.observableArray([]);
self.DeleteAdapters = ko.observableArray([]);
self.NewAdapter = ko.observable(new Adapter("", "", "", ""));
self.Message = ko.observable("");
self.SetAdaptersFromJSON = function (jsData) {
self.Adapters = ko.mapping.fromJS(jsData);
};
//#region Edit List Options: confirmChanges
self.confirmChanges = function () {
if (self.NewAdapter().ID == 0) {
self.Adapters.push(self.NewAdapter());
}
};
//#endregion
//#region Adapter List Options: addAdapter, selectItem, deleteItem, saveAll
self.addAdapter = function () {
self.NewAdapter(new Adapter("", "", "", ""));
};
self.selectItem = function (item) {
self.NewAdapter(item);
};
self.deleteItem = function(item) {
self.DeleteAdapters.push(item.ID());
self.Adapters.remove(item);
};
self.saveAll = function () {
if (self.Adapters && self.Adapters().length > 0) {
var filtered = ko.utils.arrayFilter(self.Adapters(),
function(adapter) {
return ((!isEmpty(adapter.Manufacturer())) &&
(!isEmpty(adapter.Name())) &&
(!isEmpty(adapter.DeviceIDs()))
);
}
);
var updateSuccess = true;
if (self.DeleteAdapters().length > 0) {
jsonData = ko.toJSON(self.DeleteAdapters());
$.ajax({
url: "/api/AdapterList",
cache: false,
type: "DELETE",
data: jsonData,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function () { updateSuccess = true; },
error: function () { updateSuccess = false; }
});
}
var jsonData = ko.toJSON(filtered);
$.ajax({
url: "/api/AdapterList",
type: "POST",
data: jsonData,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function(data) {
self.SetAdaptersFromJSON(data);
updateSuccess = true && updateSuccess;
},
error: function () { updateSuccess = false; }
});
if (updateSuccess == true) { self.Message("Update Successfull"); }
else { self.Message("Update Failed"); }
}
};
//#endregion
}
//#endregion
ko.bindingHandlers.message = {
update: function(element, valueAccessor) {
$(element).hide();
ko.bindingHandlers.text.update(element, valueAccessor);
$(element).fadeIn();
$(element).fadeOut(4000);
}
};
ko.bindingHandlers.sortTable = {
init: function (element, valueAccessor) {
setTimeout(function () {
$(element).addClass('tablesorter');
$(element).tablesorter({ widgets: ['zebra'] });
}, 0);
}
};
function isEmpty(obj) {
if (typeof obj == 'undefined' || obj === null || obj === '') return true;
if (typeof obj == 'number' && isNaN(obj)) return true;
if (obj instanceof Date && isNaN(Number(obj))) return true;
return false;
}
无法更新我的html表的特定脚本部分是:
self.deleteItem = function(item) {
self.DeleteAdapters.push(item.ID());
self.Adapters.remove(item);
};
除了删除之外,一切似乎都有效,所以我似乎不知道接下来要看什么,而且我对javascript或淘汰赛太新了,不知道这是否是一个线索:如果我运行ko.applyBindings在self.deleteItem函数中的()命令,我得到了更新,但它确实给了我一个未处理的错误:
未捕获错误:无法解析绑定。 消息:ReferenceError:未定义消息; 绑定值:消息:消息
消息是在绑定之前在VM中定义的......有没有我错过了这一切?
答案 0 :(得分:1)
在Js文件的开头,您定义了var viewModel = new AdaptersModel();但是你说你的函数Adapter()是你的区域声明中的视图模型。它使您的代码难以阅读。我将再次尝试解决问题,但我建议您的viewmodel包含适配器,并且您的模型包含每个适配器应该是类的类实例。
您获得的具体错误是因为您将Message()绑定到某个内容然后删除Message()。你可以做的一件事就是将你的div改为:
<div id="MessageDiv" data-bind="with: Message">
<h5 data-bind="message: $data"><h5>
</div>
如果您可以创建一个小提琴,我可以给出一个更明确的原因示例,但基本上如果Message()为空,则with binding不应显示删除后未定义的标题。
你可能需要做的是查看作为'item'发送的内容,并确保它不是你的viewmodel。
self.deleteItem = function(item) {
console.log(item); // << Check console and see what is being returned
self.DeleteAdapters.push(item.ID());
self.Adapters.remove(item);
};
您可能只删除了一个以上的适配器。
这会引导您找到正确的方向,但我会认真考虑重命名您的代码。
答案 1 :(得分:1)
解决周围问题有很多帮助,但实际上没有解决问题的“原因”。更新有时完美,但不是其他时间。当我对它进行故障排除并开始让它变得愚蠢并在JSFiddle中工作时,我没有在所有工作版本中包含data-bind="sortTable: true"
。显然,如果您按照我的方式对表进行排序或使用代码,它将无法正常工作。我见过的示例代码位于http://jsfiddle.net/gregmason/UChLF/16/,相关代码:
ko.bindingHandlers.tableSorter = {
init: function (element) {
setTimeout(function () { $(element).tablesorter(); }, 0);
},
update: function (element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor()); //just to get a dependency
$(element).trigger("update");
}
};
通过单击行上的删除链接可以明显显示错误行为。
这可以通过绑定每个表头而不是表本身来处理,并将tableSorter
代码替换为此线程中讨论的自定义sort
行为:
knockout js - Table sorting using column headers。排序替换在这里:
ko.bindingHandlers.sort = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var asc = false;
element.style.cursor = 'pointer';
element.onclick = function(){
var value = valueAccessor();
var prop = value.prop;
var data = value.arr;
asc = !asc;
if(asc){
data.sort(function(left, right){
return left[prop]() == right[prop]() ? 0 : left[prop]() < right[prop]() ? -1 : 1;
});
} else {
data.sort(function(left, right){
return left[prop]() == right[prop]() ? 0 : left[prop]() > right[prop]() ? -1 : 1;
});
}
}
}
};
这修复了我的排序/编辑/删除问题,并且工作的jsFiddle在这里:http://jsfiddle.net/gregmason/UChLF/18/