我在页面上有四个控件,一个简单的表单,有名字和姓氏,出生日期和这个下拉列表,其中包含一些国家的名称。当我对这些控件进行更改时,我可以在我的viewModel中看到这些更改,这些更改在下面的SavePersonDetails帖子中作为参数传递,但我从未在该视图模型中看到LocationId更新,我也不确定为什么。
这是我的标记Index.cshtml中的内容:
@model Mvc4withKnockoutJsWalkThrough.ViewModel.PersonViewModel
@using System.Globalization
@using Mvc4withKnockoutJsWalkThrough.Helper
@section styles{
@Styles.Render("~/Content/themes/base/css")
<link href="~/Content/Person.css" rel="stylesheet" />
}
@section scripts{
@Scripts.Render("~/bundles/jqueryui")
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/Application/Person.js"></script>
<script type="text/javascript">
Person.SaveUrl = '@Url.Action("SavePersonDetails", "Person")';
Person.ViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
var userObject = '@Html.Raw(Json.Encode(Model))';
var locationsArray = '@Html.Raw(Json.Encode(Model.Locations))';
var vm = {
user : ko.observable(userObject),
availableLocations: ko.observableArray(locationsArray)
};
ko.applyBindings(vm);
</script>
}
<form>
<p data-bind="with: user">
Your country:
<select data-bind="options: $root.availableLocations, optionsText: 'Text', optionsValue: 'Value', value: LocationID, optionsCaption: 'Choose...'">
</select>
</p>
</form>这是我的视图模型:
public class PersonViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string LocationId { get; set; }
public IEnumerable<SelectListItem> Locations { get; set; }
}我有一个加载Person的简单控制器和一个包含三个国家/地区的下拉列表。
private PersonViewModel _viewModel;
public ActionResult Index()
{
var locations = new[]
{
new SelectListItem { Value = "US", Text = "United States" },
new SelectListItem { Value = "CA", Text = "Canada" },
new SelectListItem { Value = "MX", Text = "Mexico" },
};
_viewModel = new PersonViewModel
{
Id = 1,
FirstName = "Test",
LastName = "Person",
DateOfBirth = new DateTime(2000, 11, 12),
LocationId = "", // I want this value to get SET when the user changes their selection in the page
Locations = locations
};
_viewModel.Locations = locations;
return View(_viewModel);
}
[HttpPost]
public JsonResult SavePersonDetails(PersonViewModel viewModel)
{
int id = -1;
SqlConnection myConnection = new SqlConnection("server=myMachine;Trusted_Connection=yes;database=test;connection timeout=30");
try
{
// omitted
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
return Json(id, "json");
}最后,这是我的Person.js文件,我正在使用knockout
var Person = {
PrepareKo: function () {
ko.bindingHandlers.date = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
element.onchange = function () {
var observable = valueAccessor();
observable(new Date(element.value));
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var observable = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(observable);
if ((typeof valueUnwrapped == 'string' || valueUnwrapped instanceof String) && valueUnwrapped.indexOf('/Date') === 0) {
var parsedDate = Person.ParseJsonDate(valueUnwrapped);
element.value = parsedDate.getMonth() + 1 + "/" + parsedDate.getDate() + "/" + parsedDate.getFullYear();
observable(parsedDate);
}
}
};
},
ParseJsonDate: function (jsonDate) {
return new Date(parseInt(jsonDate.substr(6)));
},
BindUIwithViewModel: function (viewModel) {
ko.applyBindings(viewModel);
},
EvaluateJqueryUI: function () {
$('.dateInput').datepicker();
},
RegisterUIEventHandlers: function () {
$('#Save').click(function (e) {
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: Person.SaveUrl,
data: ko.toJSON(Person.ViewModel),
contentType: 'application/json',
async: true,
beforeSend: function () {
// Display loading image
},
success: function (result) {
// Handle the response here.
if (result > 0) {
alert("Saved");
} else {
alert("There was an issue");
}
},
complete: function () {
// Hide loading image.
},
error: function (jqXHR, textStatus, errorThrown) {
// Handle error.
}
});
}
});
},
};
$(document).ready(function () {
Person.PrepareKo();
Person.BindUIwithViewModel(Person.ViewModel);
Person.EvaluateJqueryUI();
Person.RegisterUIEventHandlers();
});如您所见,我在页面中有数据,但没有一个数据显示为选中状态

发布于 2013-11-26 01:38:02
您的解决方案过于复杂,并且会导致数据的某些奇怪之处。与其试图修补泰坦尼克号,你最好的选择是重新开始并简化:
页面的模型包含了所需的所有信息。不需要尝试创建两个完全独立的视图模型来处理用户数据和位置。使用映射插件,您可以为主视图模型中的不同对象指定不同的“视图模型”,并且可以遵循一个更简单的模式来设置所有这些。这就是我要做的:
// The following goes in external JS file
var PersonEditor = function () {
var _init = function (person) {
var viewModel = PersonEditor.PersonViewModel(person);
ko.applyBindings(viewModel);
_wireEvents(viewModel);
}
var _wireEvents = function (viewModel) {
// event handlers here
}
return {
Init: _init
}
}();
PersonEditor.PersonViewModel = function (person) {
var mapping = {
'Locations': {
create: function (options) {
return new PersonEditor.LocationViewModel(options.data)
}
}
}
var model = ko.mapping.fromJS(person, mapping);
// additional person logic and observables
return model;
}
PersonEditor.LocationViewModel = function (location) {
var model = ko.mapping.fromJS(location);
// additional location logic and observables
return model;
}
// the following is all you put on the page
<script src="/path/to/person-editor.js"></script>
<script>
$(document).ready(function () {
var person = @Html.Raw(@Json.Encode(Model))
PersonEditor.Init(person);
});
</script>然后,将选择列表绑定到位置数组所需的就是:
<p>
Your country:
<select data-bind="options: Locations, optionsText: 'Text', optionsValue: 'Value', value: LocationId, optionsCaption: 'Choose...'">
</select>
</p>发布于 2013-11-27 14:28:56
根据您更新的问题,执行此操作。
1.我们实际上并不需要locationsArray。它已经在user.Locations中了(愚蠢的我)
2.在Index.cshtml页面上,如下所示更改JavaScript。
var userObject = @Html.Raw(Json.Encode(Model)); // no need for the quotes here
jQuery(document).ready(function ($) {
Person.PrepareKo();
Person.EvaluateJqueryUI();
Person.RegisterUIEventHandlers();
Person.SaveUrl = "@Url.Action("SavePersonDetails", "Knockout")";
Person.ViewModel = {
user : ko.observable(userObject)
};
Person.BindUIwithViewModel(Person.ViewModel);
});3.在您的Person.js页面上,在RegisterUIEventHandlers #Save按钮单击事件中执行以下操作。
$('#Save').click(function (e) {
var updatedUser = Person.ViewModel.user();
updatedUser.Locations.length = 0; // not mandatory, but we don't need to send extra payload back to server.
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: Person.SaveUrl,
data: ko.toJSON(updatedUser), // Data Transfer Object
contentType: 'application/json',
beforeSend: function () {
// Display loading image
}
}).done(function(result) {
// Handle the response here.
if (result > 0) {
alert("Saved");
} else {
alert("There was an issue");
}
}).fail(function(jqXHR, textStatus, errorThrown) {
// Handle error.
}).always(function() {
// Hide loading image.
});
}
});5.最后,我们的标记
<p data-bind="with: user">
Your country:
<select data-bind="options: Locations,
optionsText: 'Text',
optionsValue: 'Value',
value: LocationId,
optionsCaption: 'Choose...'">
</select>
</p>在一个不相关的附注中,
从jQuery 1.8开始,不推荐使用jqXHR.success()、jqXHR.error()和jqXHR.complete()回调函数。要为最终删除它们的代码做好准备,请改用jqXHR.done()、jqXHR.fail()和jqXHR.always()。
https://stackoverflow.com/questions/20187451
复制相似问题