我正在尝试使用ASP.NET MVC 4和KnockoutJS为数据记录创建单页面编辑器。使用显示记录的表格和编辑单个记录的表单非常简单。
点击'编辑'编辑表单更新的记录,数据持久保存到数据库没有问题。此后有两个问题:
我不知道如何解决(1)。对于(2),在Knockout完成后,有一些编写通用扩展方法或函数来清除任何形式的方法。我可以很容易地使用JQuery做到这一点,但我可能会错过Knockout已经可以做的事情。
页面代码如下:
@model IEnumerable<SiteDto>
@{
ViewBag.Title = "Index";
}
<h2>Sites</h2>
<table>
<caption>Sites</caption>
<thead>
<tr>
<th>Name</th>
<th>Link</th>
<th>Url</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: sites">
<tr>
<td><span data-bind="text: id"></span></td>
<td><span data-bind="text: name"></span></td>
<td><span data-bind="text: url"></span></td>
<td><button data-bind="click: $parent.selectItem">Edit</button></td>
</tr>
</tbody>
</table>
<div data-bind="with: selectedItem">
<table>
<caption data-bind="text: name"></caption>
<tbody>
<tr>
<td><input data-bind="value: id" /></td>
</tr>
<tr>
<td><input data-bind="value: url" /></td>
</tr>
<tr>
<td><input data-bind="value: name" /></td>
</tr>
</tbody>
</table>
<button data-bind="click: save">Save</button>
</div>
<script type="text/javascript">
function viewModel() {
var sites = ko.observableArray(@Html.Raw(Model.ToJson()));
var selectedItem = ko.observable();
selectItem = function (s) {
selectedItem(s);
};
save = function () {
alert(ko.toJSON(selectedItem));
$.ajax({
url: "/Home/Save",
type: "POST",
data: ko.toJSON(selectedItem),
contentType: "application/json",
dataType: "json",
success: function(result) {
alert(result);
},
error: function() {
alert("fail");
}
});
};
return {
sites: sites,
selectedItem: selectedItem,
selectItem: selectItem,
save: save
}
}
ko.applyBindings(viewModel);
</script>
答案 0 :(得分:2)
我会一次一个地回答你的观点,因为它们并没有真正相关。
1)这里的问题是,您使用ASP.NET MVC模型,并将其放在observableArray中。问题是,如果添加,删除或交换项目,observableArray将更新UI,但它不会通知UI对单个项目的更改。因此,即使您正在正确编辑行,UI也永远不会知道。理想的解决方案是不是简单地将MVC模型注入observableArray,而是将模型映射到数据结构,其中项目(id,url,name)的可编辑属性是可观察的。未经测试的演示代码:
var rawSites = @Html.Raw(Model.ToJson()),
sites = ko.observableArray(rawSites.map(function (rawSite) {
return {
id: ko.observable(rawSite.id),
url: ko.observable(rawSite.url),
name: ko.observable(rawSite.name)
};
}));
编辑:我的原始答案提出了第二种方法,即通过从observableArray中删除已编辑的项目并重新添加来“破解”UI更新。 @Tomalak在评论中提出了更好的建议:在项目上使用valueHasMutated()
。结果是一样的,但它不那么hacky。请注意,我认为上述解决方案仍然更可取,因为它会表现更好(需要更少的UI重排),并且当您稍后为此代码添加更多功能时它会更强大。
2)取决于你想要的东西。您希望编辑表单保持可见还是消失?您已经在使用with: selectedItem
绑定,这使得消失行为变得非常简单:只需从selectItem(null)
成功回调中调用save
即可。如果您希望表单始终保持可见,并且只是清除字段,我想以下方法可行:
function viewModel() {
var sites = ko.observableArray(@Html.Raw(Model.ToJson()));
var originalItem = null;
var selectedItem = {
id: ko.observable(),
url: ko.observable(),
name: ko.observable()
};
var selectItem = function (s) {
// This function now copies the properties instead of using the item itself
selectedItem.id(ko.unwrap(s.id));
selectedItem.url(ko.unwrap(s.url));
selectedItem.name(ko.unwrap(s.name));
// Get a reference to s so we can update it when we are done editing
originalItem = s;
};
var resetSelectedItem = function () {
// Clear the form and reset the reference we held earlier
selectItem({
id: null,
url: null,
name: null
});
originalItem = null;
};
save = function () {
alert(ko.toJSON(selectedItem));
$.ajax({
url: "/Home/Save",
type: "POST",
data: ko.toJSON(selectedItem),
contentType: "application/json",
dataType: "json",
success: function(result) {
alert(result);
// Done editing: update the item we were editing
originalItem.id(selectedItem.id());
originalItem.url(selectedItem.url());
originalItem.name(selectedItem.name());
// Clear the form
resetSelectedItem();
},
error: function() {
alert("fail");
// Clear the form
resetSelectedItem();
}
});
};
return {
sites: sites,
selectedItem: selectedItem,
selectItem: selectItem,
save: save
}
}