背景:我对MVC& amp; Knockout.js但我正在努力加快这些技术的速度。我正在使用MVC 5和EF6以及Knockout.JS 3.2。
我有一个Detail视图,根据URL中传递的ID使用MVC提取“VoteAnswer”对象:
例如,我可以转到url MyDomain / VoteAnswers / Details / 1,它将正确地从我的数据库中提取信息(它将ID为1的VoteAnswer)并显示在我的详细信息视图中。但是我试图连接我的Knockout.js“VoteAnswer”ViewModel以同样的方式运行并遇到麻烦。
这是我的详细信息视图:(请注意@ Html.DisplayFor(model => model.VoteAnswerId)等工作并显示数据库中的数据。
@model AM_SPA_TestSite.Models.VoteAnswer
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Details</title>
<script src="~/KnockoutViewModels/VoteAnswers.js"></script>
</head>
<body>
<div>
<h4>VoteAnswer</h4>
<hr />
<table>
<tr>
<td>Id</td>
<td data-bind="text: id"></td>
</tr>
<tr>
<td>Display Text</td>
<td data-bind="text: isActive"></td>
</tr>
<tr>
<td>IsActive</td>
<td data-bind="text: displayText"></td>
</tr>
</table>
<table>
<tr>
<td>Id</td>
<td><input type="text" data-bind="value: id" /></td>
</tr>
<tr>
<td>Display Text</td>
<td><input type="text" data-bind="value: displayText" /></td>
</tr>
<tr>
<td>IsActive</td>
<td><input type="text" data-bind="value: isActive" /></td>
</tr>
</table>
<table>
<tr>
<td>Id</td>
<td>@Html.DisplayFor(model => model.VoteAnswerId)</td>
</tr>
<tr>
<td>Display Text</td>
<td>@Html.DisplayFor(model => model.DisplayText)</td>
</tr>
<tr>
<td>IsActive</td>
<td>@Html.DisplayFor(model => model.IsActive)</td>
</tr>
</table>
</div>
这是我的Knockout.Js ViewModel
// VoteAnswer ViewModel
var VoteAnswerVM = {
id: ko.observable(),
displayText: ko.observable(),
isActive: ko.observable(),
SaveVoteAnswer: function () {
$.ajax({
url: '/VoteAnswers/Create',
type: 'post',
dataType: 'json',
data: ko.toJSON(this),
contentType: 'application/json',
success: function (result) {
},
error: function (err) {
if (err.responseText == "Creation Failed")
{ window.location.href = '/VoteAnswers/Index/'; }
else {
alert("Status:" + err.responseText);
window.location.href = '/VoteAnswers/Index/';;
}
},
complete: function () {
window.location.href = '/VoteAnswers/Index/';
}
});
}
};
//Go
$(document).ready(function () {
//initialize and create new VoteAnswerVM by URL value here?
ko.applyBindings(VoteAnswerVM);
});
我知道我缺少的是初始化ID为1的ViewModel,但我认为MVC模型已经拥有数据并且knockout.js应该映射到该数据而无需通过向数据库发送请求来手动初始化再次。我错过了什么?感谢。
编辑:已添加 解决方案 。我不确定我是否已采用这种方法,但现在确实如此。更新控制器以仅返回视图而不查询数据库。 (否则我会对同一数据进行两次数据库调用。
// GET: VoteAnswers/Details/5
public ViewResult Details(int? id)
{
return View();
}
添加了一个查询数据库的API控制器。
// GET: api/VoteAnswers/5
[ResponseType(typeof(VoteAnswer))]
public async Task<IHttpActionResult> GetVoteAnswer(int id)
{
VoteAnswer voteAnswer = await db.VoteAnswers.FindAsync(id);
if (voteAnswer == null)
{
return NotFound();
}
return Ok(voteAnswer);
}
在我的视图(.cshtml文件)中,我引用了我的knockout.js ModelView,View在下面:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Details</title>
<script src="~/KnockoutViewModels/VoteAnswers.js"></script>
</head>
<body>
<div>
<h4>VoteAnswer</h4>
<hr />
<table>
<tr>
<td>Id</td>
<td data-bind="text: VoteAnswerId"></td>
</tr>
<tr>
<td>Display Text</td>
<td data-bind="text: IsActive"></td>
</tr>
<tr>
<td>IsActive</td>
<td data-bind="text: DisplayText"></td>
</tr>
</table>
<table>
<tr>
<td>Id</td>
<td><input type="text" data-bind="value: VoteAnswerId" /></td>
</tr>
<tr>
<td>Display Text</td>
<td><input type="text" data-bind="value: DisplayText" /></td>
</tr>
<tr>
<td>IsActive</td>
<td><input type="text" data-bind="value: IsActive" /></td>
</tr>
</table>
</div>
<div id="error"></div>
</body>
</html>
更新了我的ViewModel脚本,以根据URL ID访问数据库。
// VoteAnswer ViewModel
var VoteAnswer = function () {
var self = this;
self.VoteAnswerId = ko.observable();
self.DisplayText = ko.observable();
self.IsActive = ko.observable();
self.SaveVoteAnswer = function () {
$.ajax({
url: '/VoteAnswers/Create',
type: 'post',
dataType: 'json',
data: ko.toJSON(this),
contentType: 'application/json',
success: function (result) {
},
error: function (err) {
if (err.responseText == "Creation Failed")
{ window.location.href = '/VoteAnswers/Index/'; }
else {
alert("Status:" + err.responseText);
window.location.href = '/VoteAnswers/Index/';;
}
},
complete: function () {
window.location.href = '/VoteAnswers/Index/';
}
});
}
self.load = function (id) {
if (id != 0) {
$.ajax({
url: '/api/VoteAnswers/' + id,
type: 'get',
data: ko.toJSON(this),
contentType: 'application/json',
success: function(data) {
self.VoteAnswerId = ko.observable(data.voteAnswerId);
self.DisplayText = ko.observable(data.displayText);
self.IsActive = ko.observable(data.isActive);
ko.applyBindings(self);
},
error: function(err) {
if (err.responseText == "Creation Failed") {
window.location.href = '/VoteAnswers/Index/';
} else {
$("#error").text("Status:" + err.responseText);
//window.location.href = '/VoteAnswers/Index/';;
}
},
complete: function() {
//window.location.href = '/VoteAnswers/Index/';
}
});
} else {
window.location.href = '/VoteAnswers/Index/';
}
}
};
function GetURLParameter() {
var sPageUrl = window.location.href;
var indexOfLastSlash = sPageUrl.lastIndexOf("/");
if (indexOfLastSlash > 0 && sPageUrl.length - 1 != indexOfLastSlash)
return sPageUrl.substring(indexOfLastSlash + 1);
else
return 0;
}
//Go
$(document).ready(function () {
//initialize and create new VoteAnswerVM by URL value here?
var viewModel = new VoteAnswer();
viewModel.load(GetURLParameter());
});
答案 0 :(得分:0)
我希望我理解正确,如果我的回答错误,请告诉我错误的地方。
要实现的第一件事是,如果将KO observable绑定到输入字段,则knockout将不会查看该输入字段的初始值并将其存储在observable中。它将执行相反的操作:它将查看observable中的当前值并将其存储在输入字段的值中。在您的情况下,observable初始化时没有值,在JavaScript中表示值undefined
。因此,如果将observable绑定到已填充Razor / MVC视图模型的字段,您将立即用您的observable中存储的空值覆盖这些值。
有一种方法可以通过Razor用你的数据填充你的Knockout模型,但是它涉及内联JavaScript,并且出于多种原因这是一种不好的做法(我会根据要求详细说明)。
执行此操作的最佳方法是将视图与数据分开:不要将MVC视图模型注入视图,而是创建一个返回JSON并在那里返回数据的单独端点(此端点将接收ID参数而不是视图)。 JSON端点是从JavaScript调用的,可用于使用正确的值填充模型。
好处:关注点分离,为更敏感的前端启用视图缓存的可能性,不需要使用剃刀语法,甚至更糟糕的是,将它与内联JS结合使用。您对UI的所有数据绑定都将通过Knockout进行。我自己也学到了这一点,因为我们也开始使用剃须刀,但从长远来看,这个解决方案对于一个大项目来说并不可行。我们从不后悔总是从单独的JSON端点获取数据。
如果你不确定如何做到这一点,我可以写一些伪代码来说明这个想法。
答案 1 :(得分:0)
这些可能是可能的解决方案
1。)错误地使用了data_bind
<td><input type="text" data-bind="value: isActive" /></td> // which is wrong
<td><input type="text" data_bind="value: isActive" /></td> //data_bind is wrongly used
<强> 2)。强> 如果仍然存在问题,您可以尝试这种语法
@Html.DisplayFor(model => model.IsActive, new { data_bind = "value:IsActive" });
如果您发现仍然缺少某些内容,请提供一些详细信息。