Javascript 带有Knockout.js和MVC的jQuery日期选择器
我有一个MVC应用程序,我相处得很好。我使用knockout.js进行绑定,并在编辑页面上使用它,您也可以在其中编辑子记录。一切似乎都正常,除了单击“保存”时,所有日期选择器都重置为今天的日期。保存的数据是保存到数据库的数据,但该页面上所有日期选择器中的数据都会更改。刷新页面将显示正确的持久化数据 之前: 之后: 这是我的密码: 编辑页面:Javascript 带有Knockout.js和MVC的jQuery日期选择器,javascript,jquery,asp.net-mvc,knockout.js,Javascript,Jquery,Asp.net Mvc,Knockout.js,我有一个MVC应用程序,我相处得很好。我使用knockout.js进行绑定,并在编辑页面上使用它,您也可以在其中编辑子记录。一切似乎都正常,除了单击“保存”时,所有日期选择器都重置为今天的日期。保存的数据是保存到数据库的数据,但该页面上所有日期选择器中的数据都会更改。刷新页面将显示正确的持久化数据 之前: 之后: 这是我的密码: 编辑页面: @model SPMVC.Models.SeasonViewModel @using Newtonsoft.Json @{ ViewBag.T
@model SPMVC.Models.SeasonViewModel
@using Newtonsoft.Json
@{
ViewBag.Title = "Edit Season";
}
@{ string data = JsonConvert.SerializeObject(Model); }
@section scripts
{
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css">
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery-ui-1.11.1.js"></script>
<script src="~/Scripts/knockout-3.2.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/moment.js"></script>
<link href="~/Content/select2.css" rel="stylesheet" />
<script src="~/Scripts/select2.js"></script>
<script>
$(document).ready(function () { $("#e1").select2(); });
</script>
<script src="~/Scripts/seasonsviewmodel.js"></script>
<script type="text/javascript">
var seasonViewModel = new SeasonViewModel(@Html.Raw(data));
ko.applyBindings(seasonViewModel);
</script>
}
@Html.Partial("_EditSeason")
有人能解释为什么会发生这种行为吗
感谢您抽出时间阅读这篇文章
Paul。如果不查看控制器方法,就很难判断这是否是确切的问题,但我在一个项目中遇到了类似的问题 它与MVC默认JSON格式化程序的工作方式有关,而不是JSON.NET
JsonConvert.SerializeObject
在ajax调用上的success
函数中,您可以访问数据。季节视图模型
(我假设它是传递给“/seasures/Save/”调用的数据集的一个可能被操纵的副本),您可能会得到所有日期的MVC默认序列化日期格式(看起来像“/date(1239018869048)/”)。因此,您的淘汰模型在返回时将使用此值初始化,而不是在首次加载页面时由JsonConvert.SerializeObject
创建的更有用的格式
现在,当自定义日期选择器绑定的update
函数从可观察数据中提取此值时,它使用新日期(ko.utils.unwrapObservable(valueAccessor())
,它将尝试从MVC默认序列化日期格式创建日期,但失败
如果是这种情况,解决方案是将MVC的默认JSON格式化程序切换为使用JSON.NET,或者创建一个JsonResult,使用JSON.NET对这些调用进行序列化
几个例子:
window.location=data.newLocation的用途不清楚,但我猜在重新加载页面后,日期选择器只是默认为当前日期。要么删除它,要么在那一点之后检查绑定。谢谢Mark,我正在学习Pluralsight课程(观看并遵循课程,现在开始尝试我自己的模型),我自己也不能100%确定为什么会有这些代码。我把它注释掉了,我仍然得到同样的行为。我假设
if(data.sequenceviewmodel!=null)ko.mapping.fromJS(data.sequenceviewmodel,{},self)代码>是否会相应地重新绑定控件?根据评论我注意到的一件事是日期返回为date2。也许这就是问题所在?哇,你的问题中有很多代码。你有没有可能把它精简到一个更小的版本?当然,我可以试着明天再来做。我之所以发布这些信息,是因为我边走边学习,不想错过任何可能导致问题的东西。我很欣赏它给建议答案增加了复杂性,但我希望它能真正帮助我解决问题。这正是正在发生的事情。您指向我的帖子在某种程度上帮助了我,但JSON.Net似乎在我的子对象点击save controller操作后丢失了它们。我实现了JsonDotNetValueProviderFactory并添加了ValueProviderFactorys.Factorys.Remove(ValueProviderFactorys.Factorys.OfType().FirstOrDefault());valueProviderFactorys.Factories.Add(新的JsonDotNetValueProviderFactory());转到Global.asx。我需要从外观上进一步调查这件事。尽管如此,还是要感谢你迄今为止的帮助,至少让我看看问题所在。我设法找到了解决办法。你觉得这个怎么样?在save action controller方法中,我将最后一行(return)替换为:var json=Newtonsoft.json.JsonConvert.SerializeObject(季节视图模型);返回Json(新的{Json})代码>这似乎对我很有效。您认为使用此代码以后会出现任何问题吗?谢谢。这会有用的,但是为了可重用性起见,最好将其封装在自定义JsonResult中,如下面的答案所示:。这样,就不会有分散在一堆控制器上的JsonConvert调用。不过,在这一点上,这是一个偏好的问题。谢谢。我已把你的答案标为正确答案。
<h2>@ViewBag.Title</h2>
<p data-bind="text: MessageToClient"></p>
<div class="form-group">
<label class="control-label" for="SeasonDescription">Season Description</label>
<input class="form-control" name="SeasonDescription" id="SeasonDescription" data-bind="value: SeasonDescription, event: {change: flagSeasonAsEdited}, hasfocus: true" />
</div>
<div class="form-group">
<label class="control-label" for="StartDate">Start Date</label>
<input class="form-control" type="text" name="StartDate" id="StartDate" data-bind="datepicker: StartDate, datepickerOptions: {dateFormat: 'DD, MM d, yy'}, event: {change: flagSeasonAsEdited}" />
</div>
<div class="form-group">
<label class="control-label" for="Publish">Publish</label>
<input class="checkbox" name="Publish" id="Publish" type="checkbox" data-bind="checked: Publish, event: {change: flagSeasonAsEdited}" />
</div>
<table class="table table-striped">
<tr>
<th>Game Description</th>
<th>Game Type</th>
<th>Game Date</th>
<th>Publish to Schedule</th>
<th>Publish Results</th>
<th><button data-bind="click: addGame" class="btn btn-info btn-xs">Add</button></th>
</tr>
<tbody data-bind="foreach: Games">
<tr>
<td class="form-group"><input class="form-control input-sm" data-bind="value: GameDescription, event: {change: flagGameAsEdited}, hasfocus: true" /></td>
<td class="form-group"><select data-bind="options: $parent.gameTypes, optionsText: 'GameTypeDescription', optionsValue: 'Id', value: GameTypeId, select2: { }, event: {change: flagGameAsEdited}"></select></td>
<td class="form-group"><input class="form-control input-sm" type="text" data-bind="datepicker: GameDate, datepickerOptions: {dateFormat: 'DD, MM d, yy'}, event: {change: flagGameAsEdited}" /></td>
<td class="form-group"><input class="checkbox" type="checkbox" data-bind="checked: PublishToSchedule, event: {change: flagGameAsEdited}"></td>
<td class="form-group"><input class="checkbox" type="checkbox" data-bind="checked: PublishResults, event: {change: flagGameAsEdited}"></td>
<td class="form-group"><button data-bind="click: $parent.deleteGame" class="btn btn-danger btn-xs">Delete</button></td>
</tr>
</tbody>
</table>
<p><button data-bind="click: save" class="btn btn-primary">Save</button></p>
<p><a href="/Admin/Seasons/" class="btn btn-default btn-sm">« Back to List</a></p>
SeasonViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, gameMapping, self);
self.save = function () {
$.ajax({
url: "/Seasons/Save/",
type: "POST",
data: ko.toJSON(self),
contentType: "application/json",
success: function (data) {
// TODO: When re mapping the view model after save, all dates are in date2??
if (data.seasonViewModel != null)
ko.mapping.fromJS(data.seasonViewModel, {}, self);
if (data.newLocation != null)
window.location = data.newLocation;
}
});
},
self.flagSeasonAsEdited = function () {
if (self.ObjectState() != ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
return true;
},
self.addGame = function () {
// Game defaults
var game = new GameViewModel({ Id: 0, GameDescription: "", GameTypeId: 1, GameDate: new Date(), PublishToSchedule: false, PublishResults: false, ObjectState: ObjectState.Added })
self.Games.push(game);
},
self.deleteGame = function (game) {
self.Games.remove(this);
if (game.Id() > 0 && self.GamesToDelete.indexOf(game.Id()) == -1)
self.GamesToDelete.push(game.Id());
};
};
GameViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, gameMapping, self);
self.flagGameAsEdited = function () {
if (self.ObjectState() != ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
return true;
};
};
GameTypeViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, gameTypeMapping, self);
ko.applyBindings(new GameTypeViewModel());
};
var gameMapping = {
'Games': {
key: function (game) {
return ko.utils.unwrapObservable(game.Id);
},
create: function (options) {
return new GameViewModel(options.data);
}
}
};
var gameTypeMapping = {
'gameTypes': {
key: function (gameType) {
return ko.utils.unwrapObservable(gameType.Id);
},
create: function (options) {
return new GameTypeViewModel(options.data);
}
}
};
var ObjectState = {
Unchanged: 0,
Added: 1,
Modified: 2,
Deleted: 3
};
// Custom Knockout binding handler for date picker
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = new Date(ko.utils.unwrapObservable(valueAccessor()));
var current = $(element).datepicker("getDate");
// Prevents the datepicker from popping up a second time
if (value - current !== 0) {
// For some stange reason, Chrome subtracts a day when first displaying the date
if (navigator.userAgent.indexOf('Chrome') > -1 && value != '') {
var date = value.getDate() + 1;
var month = value.getMonth();
var year = value.getFullYear();
value = new Date(year, month, date);
};
$(element).datepicker("setDate", value);
}
}
};
// Custom knockout binding handler for read-only date display
ko.bindingHandlers.dateString = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var allBindings = allBindingsAccessor();
var pattern = allBindings.datePattern || 'dd/MM/yyyy';
var momentObj = moment(ko.utils.unwrapObservable(value), moment.ISO_8601);
$(element).text(momentObj.format(pattern));
}
};
ko.bindingHandlers.select2 = {
init: function (element, valueAccessor, allBindingsAccessor) {
// var options = ko.toJS(valueAccessor()) || {};
// setTimeout(function () {
// $(element).select2(options);
// }, 0);
//}
var obj = valueAccessor(),
allBindings = allBindingsAccessor(),
lookupKey = allBindings.lookupKey;
$(element).select2(obj);
if (lookupKey) {
var value = ko.utils.unwrapObservable(allBindings.value);
$(element).select2('data', ko.utils.arrayFirst(obj.data.results, function (item) {
return item[lookupKey] === value;
}));
}
}
};