将 Bootstrap 日期选择器与 Knockout.js 视图模型绑定(ASP.NET 核心)
Bind Bootstrap datepicker with Knockout.js viewmodel (ASP.NET Core)
我尝试绑定 Bootstrap 日期选择器控件
但没有成功
<input class="form-control" data-bind="datePicker : Observation.ObservationDateTime" type="date" />
到 Knockout.js 视图模型。 (这是一个 ASP.NET 核心项目。)其中涉及的活动部分太多,我无法在这个特定案例中隔离问题。 我想要实现的是一个简单的双向绑定:将视图模型中的日期填充到日期选择器控件中;并且表单提交的日期被发送回 viewmodel/controller.
我可以验证日期是否已成功传递给 Knockout 视图模型。为了跟踪它的价值,我目前将它作为文本打印在页面上:
<div>Viewmodel date as text: <span data-bind="text:Observation.ObservationDateTime"></span></div>
它呈现如下:
我的研究表明,(1) 域模型、(2) 服务器端视图模型、(3) 客户端 (Knockout) 视图模型和 (4) 视图本身的设置都很重要。所以我粘贴了下面每个的相关代码:
领域模型:
[Required]
//[Display(Name = "Date/Time")]
//[DisplayFormat(DataFormatString = "{0:dddd, dd/MM/yyyy HH:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Date)]
public DateTime ObservationDateTime { get; set; }
服务器viewmodel/controller:
Observation = new Observation() { ObservationDateTime = _systemClock.Now },
客户端Knockout.js视图模型
我尝试过各种绑定处理程序。目前(见位开始 'ko.bindingHandlers.datepicker':
CreateObservationViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, observedSpeciesMapping, self);
ko.bindingHandlers.selectPicker = {
init: function (element, valueAccessor, allBindings) {
$(element).selectpicker('render');
}
};
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function (element, valueAccessor) {
var widget = $(element).data("datepicker");
//when the view model is updated, update the widget
if (widget) {
widget.date = ko.utils.unwrapObservable(valueAccessor());
if (widget.date) {
widget.setValue();
}
}
}
};
Razor 视图日期选择器Bootstrap 控件:
<div class="form-group">
<label class="control-label" for="Observation.ObservationDateTime">Date:</label>
<input class="form-control" data-bind="datePicker : Observation.ObservationDateTime" type="date" />
<div>Viewmodel date as text: <span data-bind="text: Observation.ObservationDateTime"></span></div>
</div>
我认为问题出在 Knockout 视图模型中的 datepicker bindingHandler。但是,尽管进行了数小时的修补,我还是没有解决它。有什么想法或指示吗?
更新
整个视图模型
CreateObservationViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, observedSpeciesMapping, self);
ko.bindingHandlers.selectPicker = {
init: function (element, valueAccessor, allBindings) {
$(element).selectpicker('render');
}
};
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function (element, valueAccessor) {
//when the view model is updated, update the widget
var value = ko.unwrap(valueAccessor());
$(element).val(value).datepicker("update");
}
};
self.addObservedSpecies = function () {
var observedSpecies = new ObservedSpeciesViewModel({ Id: 0, BirdId: 0, Quantity: 1 });
self.ObservedSpecies.push(observedSpecies);
};
self.removeObservedSpecies = function () {
if (self.ObservedSpecies().length > 1)
self.ObservedSpecies.pop();
};
self.disableSubmitButton = ko.observable(false);
self.Total = ko.computed(function () {
var total = 0;
total += self.ObservedSpecies().length;
return total;
}),
self.post = function () {
self.disableSubmitButton(true);
if (self.ObservedSpecies().length < 1) {
// ToDo: Implement proper client-side validation of the Observed Species collection
alert("You must choose at least one observed bird species");
self.MessageToClient("You must choose at least one observed bird species...");
self.disableSubmitButton(false);
return;
}
$.ajax({
url: "/Observation/Post/",
type: "POST",
data: ko.toJSON(self),
headers:
{
"content-type": "application/json; charset=utf-8"
},
success: function (data) {
var obj = JSON.parse(data);
if (obj.IsModelStateValid === false) {
self.MessageToClient(obj.MessageToClient);
}
else {
window.location.replace("./Index/");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
self.disableSubmitButton(false);
if (XMLHttpRequest.status === 400) {
$('#MessageToClient').text(XMLHttpRequest.responseText);
}
else {
$('#MessageToClient').text('The web server had an error. The issue has been logged for investigation by the developer.');
}
}
});
};
};
var observedSpeciesMapping = {
'ObservedSpecies': {
key: function (obsevedSpecies) {
return ko.utils.unwrapObservable(obsevedSpecies.Id);
},
create: function (options) {
return new CreateObservationViewModel(options.data);
}
}
};
ObservedSpeciesViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, observedSpeciesMapping, self);
};
Razor 视图片段
@section scripts{
<script src="~/js/knockout-3.4.2.js"></script>
<script src="~/js/knockout.mapping-latest.js"></script>
<script src="~/js/jqueryvalidate.js"></script>
<script src="~/js/jquery-validate.bootstrap-tooltip.js"></script>
<script src="~/js/createobservationviewmodel.js"></script>
<script type="text/javascript">
var createObservationViewModel = new CreateObservationViewModel(@Html.Raw(data));
ko.applyBindings(createObservationViewModel);
</script>
}
根据你的描述,我有点不确定是哪一部分不工作,但我可以看到两个问题。
第一个问题很小,可能只是一个转录错字;您在绑定定义中使用 "datepicker",在标记 data-bind 中使用 "datePicker"。绑定名称区分大小写。
第二个主要问题我认为只是日期选择器不知道您已经更新了其元素中的文本。为您的绑定的更新功能尝试这样的事情:
update: function(element, valueAccessor) {
//when the view model is updated, update the widget
var value = ko.unwrap(valueAccessor());
$(element).datepicker("update", new Date(value));
}
编辑:添加了带有绑定工作版本的片段
viewModel = function(data) {
var self = this;
self.Observation = {
ObservationDateTime: ko.observable()
};
}
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function(event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function(element, valueAccessor) {
//when the view model is updated, update the widget
var value = ko.unwrap(valueAccessor());
$(element).datepicker("update", new Date(value));
}
};
ko.applyBindings(new viewModel());
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/css/bootstrap-datepicker.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/js/bootstrap-datepicker.min.js"></script>
<input type="text" class="form-control" data-bind="datepicker : Observation.ObservationDateTime" style="width: 120px; margin:8px;" />
<div>Viewmodel date as text: <span data-bind="text: Observation.ObservationDateTime"></span></div>
<br/>
<span>Manual change: </span><input type="text" data-bind="textInput: Observation.ObservationDateTime" />
我尝试绑定 Bootstrap 日期选择器控件
但没有成功<input class="form-control" data-bind="datePicker : Observation.ObservationDateTime" type="date" />
到 Knockout.js 视图模型。 (这是一个 ASP.NET 核心项目。)其中涉及的活动部分太多,我无法在这个特定案例中隔离问题。 我想要实现的是一个简单的双向绑定:将视图模型中的日期填充到日期选择器控件中;并且表单提交的日期被发送回 viewmodel/controller.
我可以验证日期是否已成功传递给 Knockout 视图模型。为了跟踪它的价值,我目前将它作为文本打印在页面上:
<div>Viewmodel date as text: <span data-bind="text:Observation.ObservationDateTime"></span></div>
它呈现如下:
我的研究表明,(1) 域模型、(2) 服务器端视图模型、(3) 客户端 (Knockout) 视图模型和 (4) 视图本身的设置都很重要。所以我粘贴了下面每个的相关代码:
领域模型:
[Required]
//[Display(Name = "Date/Time")]
//[DisplayFormat(DataFormatString = "{0:dddd, dd/MM/yyyy HH:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Date)]
public DateTime ObservationDateTime { get; set; }
服务器viewmodel/controller:
Observation = new Observation() { ObservationDateTime = _systemClock.Now },
客户端Knockout.js视图模型
我尝试过各种绑定处理程序。目前(见位开始 'ko.bindingHandlers.datepicker':
CreateObservationViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, observedSpeciesMapping, self);
ko.bindingHandlers.selectPicker = {
init: function (element, valueAccessor, allBindings) {
$(element).selectpicker('render');
}
};
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function (element, valueAccessor) {
var widget = $(element).data("datepicker");
//when the view model is updated, update the widget
if (widget) {
widget.date = ko.utils.unwrapObservable(valueAccessor());
if (widget.date) {
widget.setValue();
}
}
}
};
Razor 视图日期选择器Bootstrap 控件:
<div class="form-group">
<label class="control-label" for="Observation.ObservationDateTime">Date:</label>
<input class="form-control" data-bind="datePicker : Observation.ObservationDateTime" type="date" />
<div>Viewmodel date as text: <span data-bind="text: Observation.ObservationDateTime"></span></div>
</div>
我认为问题出在 Knockout 视图模型中的 datepicker bindingHandler。但是,尽管进行了数小时的修补,我还是没有解决它。有什么想法或指示吗?
更新 整个视图模型
CreateObservationViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, observedSpeciesMapping, self);
ko.bindingHandlers.selectPicker = {
init: function (element, valueAccessor, allBindings) {
$(element).selectpicker('render');
}
};
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function (element, valueAccessor) {
//when the view model is updated, update the widget
var value = ko.unwrap(valueAccessor());
$(element).val(value).datepicker("update");
}
};
self.addObservedSpecies = function () {
var observedSpecies = new ObservedSpeciesViewModel({ Id: 0, BirdId: 0, Quantity: 1 });
self.ObservedSpecies.push(observedSpecies);
};
self.removeObservedSpecies = function () {
if (self.ObservedSpecies().length > 1)
self.ObservedSpecies.pop();
};
self.disableSubmitButton = ko.observable(false);
self.Total = ko.computed(function () {
var total = 0;
total += self.ObservedSpecies().length;
return total;
}),
self.post = function () {
self.disableSubmitButton(true);
if (self.ObservedSpecies().length < 1) {
// ToDo: Implement proper client-side validation of the Observed Species collection
alert("You must choose at least one observed bird species");
self.MessageToClient("You must choose at least one observed bird species...");
self.disableSubmitButton(false);
return;
}
$.ajax({
url: "/Observation/Post/",
type: "POST",
data: ko.toJSON(self),
headers:
{
"content-type": "application/json; charset=utf-8"
},
success: function (data) {
var obj = JSON.parse(data);
if (obj.IsModelStateValid === false) {
self.MessageToClient(obj.MessageToClient);
}
else {
window.location.replace("./Index/");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
self.disableSubmitButton(false);
if (XMLHttpRequest.status === 400) {
$('#MessageToClient').text(XMLHttpRequest.responseText);
}
else {
$('#MessageToClient').text('The web server had an error. The issue has been logged for investigation by the developer.');
}
}
});
};
};
var observedSpeciesMapping = {
'ObservedSpecies': {
key: function (obsevedSpecies) {
return ko.utils.unwrapObservable(obsevedSpecies.Id);
},
create: function (options) {
return new CreateObservationViewModel(options.data);
}
}
};
ObservedSpeciesViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, observedSpeciesMapping, self);
};
Razor 视图片段
@section scripts{
<script src="~/js/knockout-3.4.2.js"></script>
<script src="~/js/knockout.mapping-latest.js"></script>
<script src="~/js/jqueryvalidate.js"></script>
<script src="~/js/jquery-validate.bootstrap-tooltip.js"></script>
<script src="~/js/createobservationviewmodel.js"></script>
<script type="text/javascript">
var createObservationViewModel = new CreateObservationViewModel(@Html.Raw(data));
ko.applyBindings(createObservationViewModel);
</script>
}
根据你的描述,我有点不确定是哪一部分不工作,但我可以看到两个问题。
第一个问题很小,可能只是一个转录错字;您在绑定定义中使用 "datepicker",在标记 data-bind 中使用 "datePicker"。绑定名称区分大小写。
第二个主要问题我认为只是日期选择器不知道您已经更新了其元素中的文本。为您的绑定的更新功能尝试这样的事情:
update: function(element, valueAccessor) {
//when the view model is updated, update the widget
var value = ko.unwrap(valueAccessor());
$(element).datepicker("update", new Date(value));
}
编辑:添加了带有绑定工作版本的片段
viewModel = function(data) {
var self = this;
self.Observation = {
ObservationDateTime: ko.observable()
};
}
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function(event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function(element, valueAccessor) {
//when the view model is updated, update the widget
var value = ko.unwrap(valueAccessor());
$(element).datepicker("update", new Date(value));
}
};
ko.applyBindings(new viewModel());
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/css/bootstrap-datepicker.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.7.1/js/bootstrap-datepicker.min.js"></script>
<input type="text" class="form-control" data-bind="datepicker : Observation.ObservationDateTime" style="width: 120px; margin:8px;" />
<div>Viewmodel date as text: <span data-bind="text: Observation.ObservationDateTime"></span></div>
<br/>
<span>Manual change: </span><input type="text" data-bind="textInput: Observation.ObservationDateTime" />