i know this question is asked lot of times before... but i have tried all the solutions and none of them are working.... hence asking again...
how do i bind the json date to knockout element... below is the code i have...
#Html.Bootstrap().ControlGroup().TextBoxFor(x => x.DateOfBirth).HtmlAttributes(new { data_bind = "kodate: DateOfBirth, datepickerOptions : new Date()", #class = "datepicker" })
$(function () {
$('.datepicker').datepicker({
autoclose: true
});
});
<input data-bind="kodate: startdate" class="datepicker"/>
the kodate is defined as below
ko.bindingHandlers.kodate = {
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);
}
});
ko.utils.registerEventHandler(element, "change", function () {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(new Date(element.value));
}
});
},
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) {
return;
}
if (_.isString(widget.date)) {
widget.date = new Date(parseInt(widget.date.replace(/\/Date\((.*?)\)\//gi, "$1")));
//widget.date = new Date(widget.date);
}
widget.setValue();
}
},
update_old: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
$(element).datepicker("setValue", value);
}
};
now the date is being displayed correctly... but when i post the date... it is posted in json format and the model binder is not able to convert the json date (/Date(1339230900000)/) to the actual date and hence the date remains null on server side...
how do make sure that the model binder converts the json date /Date(1339230900000)/ to the server side DateTime or how to post the date in different format so that model binder is able to recognize the date??
interesting thing is if i change the date, then its posted in ISO format, but if i don't change the date then its posted in json format... so may be something wrong with the init code...
i am using bootstrap-datepicker: https://github.com/eternicode/bootstrap-datepicker
any help is greatly appreciated...
The easiest way to do this is using a client-side library such as Moment.js to format your date properly while maintaining the two-way data-binding offered by Knockout. A simple way to get started is using a lightweight plug-in like shown here - http://makingsense.github.io/moment-datepicker/
You are not required to use that plug-in though, you just need to write your own custom binding handler to handle getting and setting the date. An example of this that I have used in the past -
Register a binding handler to format the date to bind to -
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $(element).datepicker("getDate");
if (value - current !== 0) {
$(element).datepicker("setDate", value);
}
}
};
And then the mark-up -
<div class="span4">
<label>Start Date : </label><input data-bind="datepicker: startDate, datepickerOptions: { maxDate: new Date() }" />
</div>
Note: This answer was derived from an aggregation of other answers.
Related
I'm using knockout.js 3.3.0 with jQuery mobile 1.4.
The problem using knockout.js together with jQuery mobile is that the programmatically changes to the underlying viewmodel are not always reflected to the graphical user interface, due to the JQM refactoring of the html elements, or widgets. So, for example, the JQM selectmenu is synchronized from the user interface to the viewmodel, but not in the other way.
I'm trying to stick together the 3.3.0 KO 'options' binding with a custombinding to the current JQM actual version.
There are already two possible solutions for the 'refresh' problem already proposed at SO for 2.x versions of KO: jqmSelect and jqmValue, as custombindings.
I try this suggestions for a more recent KO+JQM combination, putting together all the answers/comments found at SO regarding this topic.
This is the js that i'm using to test:
$(document).ready(function () {
var jsonResultData = [{
"id": 6,
"name": "First item",
"pattern": "Cheetah"
}, {
"id": 2,
"name": "Second item",
"pattern": "Viper"
}, {
"id": 1,
"name": "Third item",
"pattern": "Scorpion"
}];
ko.applyBindings(new AdminViewModel(jsonResultData));
});
function Match(data) {
this.id = ko.observable(data.id);
this.pattern = ko.observable(data.pattern);
this.name = ko.observable(data.name);
}
function AdminViewModel(allData) {
var self = this;
self.matches = ko.observableArray([]);
self.matchesFromDb = $.map(allData, function (item) {
return new Match(item);
});
self.matches = self.matchesFromDb;
self.selectedMatchId = ko.observable(self.matches[0].id());
self.selectedMatch = ko.observable(self.matches[0]);
self.setSelectedMatchId = function (match) {
if (match.id() != self.selectedMatchId()) {
self.selectedMatchId(match.id());
self.selectedMatch(match);
}
};
self.patternValues = ko.observableArray(["Shark", "Viper", "Chameleon", "Cheetah", "Scorpion"]);
}
I made a fiddle to test the jqmValue custom binding, which is one of the latest soultions found at SO, but but i'm not able to get it to work:
ko.bindingHandlers.jqmValue = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
if (typeof ko.bindingHandlers.value.init !== 'undefined') {
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, viewModel);
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var instance;
if (typeof ko.bindingHandlers.value.update !== 'undefined') {
ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel);
}
instance = $.data(element, 'mobile-selectmenu');
if (instance) {
$(element).selectmenu('refresh', true);
}
}
};
(the original code for AdminViewModel as reference for KO 2.x thanks to pablo is working with the suggested change to invert value/options in the markup thanks to JohnEarles at google groups)
Here is the fiddle with actual KO and JQM versions where i try to include the best of all suggestions found regarding this topic:
http://jsfiddle.net/nHNzL/42/
but i'm still streching my hairs without success. Why in my test fiddle the changes to the viewmodel are not reflected to the JQM selectmenu? What is my error?
UPDATE: two-page fiddle to test also initialization: http://jsfiddle.net/nHNzL/50/
FINAL VERSION:
I made 2 small fixes and 1 change:
1) isInstance shall be checked every time
2) removed the if (currentValue == value)
3) inverted the disabled attribute
Moreover: i tested this custombinding in a ko foreach-loop, each select element needs to be child of an own container div.
ko.bindingHandlers.jqmSelectMenu = {
init: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
valueObservable = allBindings.get("value"), valueSubscription,
optionsObservable = allBindings.get("options"), optionsSubscription;
var refresh = function () {
var $el = $(element);
var isInstance = !!$.data(element, 'mobile-selectmenu');
if (isInstance) {
$el.selectmenu('refresh', true);
} else {
/* instantiate the widget unless jqm has already done so */
$(element).selectmenu(options);
}
};
refresh();
/* hook up to the observables that make up the underlying <select> */
if (ko.isSubscribable(valueObservable)) {
valueSubscription = valueObservable.subscribe(refresh);
}
if (ko.isSubscribable(optionsObservable)) {
optionsSubscription = optionsObservable.subscribe(refresh);
}
/* properly dispose of widgets & subscriptions when element is removed */
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).selectmenu("destroy");
if (valueSubscription) valueSubscription.dispose();
if (optionsSubscription) optionsSubscription.dispose();
});
},
update: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
$el = $(element);
/* update any widget options if necessary */
ko.utils.objectForEach(options, function (key, value) {
if (key === "enabled") {
$el.selectmenu(value ? "enable" : "disable");
} else {
$el.selectmenu("option", key, value);
}
});
}
};
In fact you are mixing two separate things.
There is the underlying <select> element. It holds the available options as well as the selected one.
Then there is the jQuery Mobile SelectMenu widget. It is concerned with the UX part. A look at the API options it provides reveals that it really has not a lot to do with the underlying select box, mostly using it for self-initialization.
Of course the widget communicates a value change to the underlying select box, but when you change the value programmatically you must communicate that to the widget yourself, by calling refresh on it. So even without knockout this is not a two-way communication.
We do have set of knockout built-in bindings that work very well with select boxes, but what we don't have is a binding that communicates changes in those values to the widget. What we also don't have is a way of initializing/updating any of the widget's API options.
So instead of re-inventing the value binding we need one that deals with the widget itself and otherwise simply complements the existing bindings:
ko.bindingHandlers.jqmSelectMenu = {
init: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
valueObservable = allBindings.get("value"), valueSubscription,
optionsObservable = allBindings.get("options"), optionsSubscription,
isInstance = !!$.data(element, 'mobile-selectmenu'),
refresh = function () { $(element).selectmenu('refresh', true); };
// instantiate the widget unless jqm has already done so
if (!isInstance) $(element).selectmenu(options);
refresh();
// hook up to the observables that make up the underlying <select>
if (ko.isSubscribable(valueObservable)) {
valueSubscription = valueObservable.subscribe(refresh);
}
if (ko.isSubscribable(optionsObservable)) {
optionsSubscription = optionsObservable.subscribe(refresh);
}
// properly dispose of widgets & subscriptions when element is removed
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).selectmenu("destroy");
if (valueSubscription) valueSubscription.dispose();
if (optionsSubscription) optionsSubscription.dispose();
});
},
update: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
$elem = $(element);
// update any widget options if necessary
ko.utils.objectForEach(options, function (key, value) {
var currentValue = $elem.selectmenu("option", key);
if (currentValue !== value) {
if (key === "disabled") {
$elem.selectmenu(value ? "disable" : "enable");
} else {
$elem.selectmenu("option", key, value);
}
}
});
}
};
I have written this in the spirit of the knockout-jQueryUI. I recommend taking a look at that library.
Here's an updated fiddle, too. http://jsfiddle.net/nHNzL/46/
I found a custom binding that makes an observable update in an editable div.
I'm not able to run a function when an event occurs with it.
Does anyone know what I can do to my custom binding "editableText" run a function in my ViewModel?
I would like the function "nameChange" to run when text is changed.
HTML:
<div contenteditable="true" data-bind="event: { change: nameChange }, editableText: firstName"></div>
Javascript:
//Editable Text Custom Binding
ko.bindingHandlers.editableText = {
init: function (element, valueAccessor) {
$(element).on('blur', function () {
var observable = valueAccessor();
observable($(this).text());
});
},
update: function (element, valueAccessor, allBindingsAccessor, data) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).text(value);
}
};
//Knockout ViewModel
function viewModel(){
var self = this;
self.firstName = ko.observable();
self.status = ko.observable();
self.nameChange = function(){
console.log("Name has been updated");
ko.mapping.fromJS("Name has been updated", {}, self.status)
}
self.loadName = function(){
ko.mapping.fromJS("hey", {}, self.firstName)
}
}
var vm = new viewModel();
ko.applyBindings(vm);
vm.loadName();
JSFIDDLE:
http://jsfiddle.net/madscientist1882/urLd2/
How about subscribing to changes on the observable? (Look at explicitly subscribing to observables)
self.firstNameSubscription = self.firstName.subscribe(function (newValue){
//do something
});
If you do this you need to dispose of the subscription when your view model goes down
self.firstNameSubscription.dispose();
If you want the observable to be updated every time a key is entered have a look here
My personal opinon is that using the variable name 'self' is probably a bad idea...
In Continuation of
knockout js bind with datetimepicker gives an exception
I am now able to use the datetimepicker with knockout but I am unable to use the time picker option of the same tool the code I have tried is embedded into the following jsfiddle but is throwing an error
<code>
http://jsfiddle.net/saqibshakil/scdET/
</code>
check console after edit
Looks like calling getDate on timepicker does not return an actual Date.
It appears that you can call it using datetimepicker successfully. So, your binding would look like:
ko.bindingHandlers.timepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize timepicker with some optional options
var options = allBindingsAccessor().timepickerOptions || {};
$(element).timepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datetimepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).timepicker("destroy");
});
},
//update the control when the view model changes
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datetimepicker("getDate");
if (value - current !== 0) {
$(element).datetimepicker("setDate", value);
}
}
};
Updated sample: http://jsfiddle.net/rniemeyer/L3BNw/
I am trying to implement a custom validation on my ASP.NET MVC3 form.
The first custom validation is only validating if a file has been selected in the file upload input.
It worked fine when I had only one client validation method. When I tried to add a second one. The second validation method is never triggered.
The GetValidationRules method in my attribute class
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "file",
ErrorMessage = "ResumeEmptyError".Translate("fordia_subform")
};
var rule2 = new ModelClientValidationRule
{
ValidationType = "extension",
ErrorMessage = "ResumeFileFormatError".Translate("fordia_subform")
};
var list = new List<ModelClientValidationRule>();
list.Add(rule2);
list.Add(rule);
return list;
}
My javascript code in my view
<script type="text/javascript">
jQuery.validator.addMethod("file", function (value, element) {
return $('#ResumeFileName').val() != '';
});
jQuery.validator.addMethod("extension", function (value, element) {
return $('#ResumeFileName').val() == 'a';
});
jQuery.validator.unobtrusive.adapters.add("file", function (options) {
options.rules["file"] = options.params.param;
if (options.message) {
options.messages['file'] = options.message;
}
});
jQuery.validator.unobtrusive.adapters.add("extension", function (options) {
options.rules["extension"] = options.params.param;
if (options.message) {
options.messages["extension"] = options.message;
}
});
</script>
When I look at my HTML source, I have the following HTML attributes on my input element :
<input data-val="true" data-val-extension="Erreur: format error" data-val-file="Required" id="Resume" name="Resume" type="file" value="" class="input-validation-error">
What am I missing to have multiple client validation methods on this form?
In the script you have shown you seem to be using some options.params.param parameter which is never declared nor passed from your validation attribute. So at its present form your script won't work even with a single rule. You said it was working but I guess you must have changed something in the code because what you have shown has no chance of working.
So if you don't have parameters here's what you could do (notice the empty array passed as second argument to the add adapter method):
jQuery.validator.addMethod("file", function (value, element) {
return $('#ResumeFileName').val() != '';
});
jQuery.validator.unobtrusive.adapters.add("file", [], function (options) {
options.rules["file"] = options.params;
if (options.message) {
options.messages['file'] = options.message;
}
});
jQuery.validator.addMethod("extension", function (value, element) {
return $('#ResumeFileName').val() == 'a';
});
jQuery.validator.unobtrusive.adapters.add("extension", [], function (options) {
options.rules["extension"] = options.params;
if (options.message) {
options.messages["extension"] = options.message;
}
});
and if you have parameters you will need to declare them first on the validation rules returned by the attribute and then use them in the adapter as shown in this post.
I'm using a jQuery UI datepicker. The HTML input field behind it is currently hooked up to KnockoutJS as a dependentObservable, but when its value is set in the viewmodel, the datepicker loses its format.
How should I do this and not lose the format? I would like the viewModel not to know that it is a jQuery datepicker.
You could write a custom binding that sets the date in the field using the datepicker APIs and also sets the value of your observable by reading the date properly.
The custom binding might look like:
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {},
$el = $(element);
//initialize datepicker with some optional options
$el.datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function() {
var observable = valueAccessor();
observable($el.datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element),
current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
You would use it like:
<input data-bind="datepicker: myDate, datepickerOptions: { minDate: new Date() }" />
The datepickeroptions would be optional and could include anything that you want to pass into the datepicker() call.
Also, this assumes that you are using an observable for the date. The binding has to do a little more work if you want to do a one-way binding with a non-observable, but that is unlikely.
Sample here: http://jsfiddle.net/rniemeyer/NAgNV/
I had to make a slight edit to RP Niemeyer's code to work in my code using the dateFormat option, replacing
$(element).datepicker("getDate")
With
$(element).val()
So the formatted version of the date was passed around correctly under the hood.
I've been using RP Niemeyer's code marked as the answer above, but since I've been using it I've made a few small changes to it. I thought I would post here. Maybe it will help others. It's pretty much the same, the only difference is that if the element has a value when the page loads then it will retain its value. Also, I made $elem a variable so that there will be less processing of $(element) that jQuery will have to do.
ko.bindingHandlers['jqDatePicker'] = {
'init': function(element, valueAccessor, allBindingsAccessor) {
/* Initialize datepicker with some optional options */
var options = allBindingsAccessor().jqDatePickerOptions || {},
prop = valueAccessor(),
$elem = $(element);
prop($elem.val());
$elem.datepicker(options);
/* Handle the field changing */
ko.utils.registerEventHandler(element, "change", function () {
prop($elem.datepicker("getDate"));
});
/* Handle disposal (if KO removes by the template binding) */
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$elem.datepicker("destroy");
});
},
'update': function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$elem = $(element),
current = $elem.datepicker("getDate");
if (value - current !== 0) {
$elem.datepicker("setDate", value);
}
}
};
Here is what worked for my particular set of circumstances. I'm running a new enough version of MVC, that the default datetime serializer renders at ISO 8601 (see On the nightmare that is JSON Dates. Plus, JSON.NET and ASP.NET Web API). My bindings do not write directly to the date picker, but instead to an input tag's "value" attribute.
Also, of note, I'm using date.js
ko.bindingHandlers.dateValue = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor(),
allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
var pattern = allBindings.datePattern || 'MM/dd/yyyy';
var date = Date.parse(valueUnwrapped)
$(element).val(date.toString(pattern));
},
init: function(element, valueAccessor, allBindingsAccessor) {
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
var date = Date.parse($(element).val());
observable(date.toString("yyyy-MM-ddThh:mm:ss"));
});
}
}
Binding looks like this:
<input class="date" type="text" data-bind="dateValue: SomeViewModelDate" />
And the JavaScript code to turn on the datepicker:
$(document).ready(function () {
$('.date').datepicker({ dateFormat: "mm/dd/yy" });
});
The datepicker samples above change the format of the date in the viewmodel from WCF format to the JavaScript date format when the user selects a new date from the datepicker control.
In my case, I was passing the date back to a WCF service, and it would not accept a deserialized JavaScript date, it needed the date in the WCF format. I modified the above script so that it stores the date in the viewmodel in WCF format so that it can be sent back to the server in that format.
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//Initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//Handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
// observable($(element).datepicker("getDate"));
// store the date in 'WCF String format"
var tempdate=$(element).datepicker("getDate");
var tempdatestr="/Date("+tempdate.getTime()+")/";
observable(tempdatestr);
});
//Handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
//Handle date data coming via JSON from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
current = $(element).datepicker("getDate");
if (value - current !== 0) {
$(element).datepicker("setDate", value);
}
}
};
One solution is to format the date yourself inside the dependentObservable function. So you must be returning something like return viewModel.someOtherObservable() in the function. Format the return value.
If you include your code, I can explain more.
Formatting the date (to mm/dd/yyyy) inside the dependentObservable is exactly what I was wondering how to do. I'll post a little of my code if you can help, Peter Mortensen and/or nEEBz.
<div data-bind="with: technology">
<div class="titleblock">
<label><b>End of Life Date</b></label>
<Input type="text" class="ui-datepicker" id="datepicker" data-bind="value: END_OF_LIFE_DATE"/>
</div>
</div>
in ViewModel - technologydetail.js:
var technology = ko.observable();
in Activate:
return dataContext.getTechnologyById(currentTechnologyId, technology);
This displays a date that looks like this in the textbox: Wed Jan 29 19:00:00 EST 2014 but
I want it to just show: 01/29/2014. I am using this datepicker option - dateFormat: "mm/dd/yy" but it has no effect on the initial value displayed.
I was able to format it using moment and it works nicely for displaying the current database value, but it seems to be breaking the binding back to the observable because it will no longer update on a SAVE.
<Input type="text" class="ui-datepicker" id="datepicker" data-bind="value: moment(END_OF_LIFE_DATE()).format('MM/DD/YYYY')" />