The radio option in my application return three values allday, morning and halfday. How can I tie my jquery datepicker as such that it is enabled only when allday is selected.
var viewModel = function () {
this.holidayType = ko.observable();
this.allday = ko.computed(
{
read: function () {
return this.holidayType() == "allday";
},
write: function (value) {
if (value)
this.holidayType("allday");
}
}, this);
this.morning = ko.computed(
{
read: function () {
return this.holidayType() == "morning";
},
write: function (value) {
if (value)
this.holidayType("morning");
}
}, this);
this.afternoon = ko.computed(
{
read: function () {
return this.holidayType() == "afternoon";
},
write: function (value) {
if (value)
this.holidayType("afternoon");
}
}, this);
};
ko.applyBindings(new viewModel());
$(function () {
$("#e1").daterangepicker({
datepickerOptions: {
minDate: 0
}
});
});
First, I added an observable named dateRangeButton to your viewmodel. This observable will hold a jQuery selector to the dynamically generated <button> that is created by the date range picker plug-in:
this.dateRangeButton = ko.observable();
Then I added a function to the viewmodel called enableDisableDateRange. When called, this function will add or remove the disabled attribute to the date range picker button based on whether or not the holidayType equals "allday":
this.enableDisableDateRange = function(context) {
context.dateRangeButton().prop('disabled', context.holidayType() != 'allday');
}
How do we get the selector for the date range picker button? I created a custom binding which will apply the date range picker to the element and populate the dateRangeButton observable in the viewmodel:
ko.bindingHandlers.datePicker = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// apply the date range picker
$(element).daterangepicker({
datepickerOptions: {
minDate: 0
}
});
// tell the viewmodel where the date range picker button is
bindingContext.$data.dateRangeButton($(element).next('button'));
// enable or disable the button
bindingContext.$data.enableDisableDateRange(bindingContext.$data);
}
};
I added this binding to the <input> element that will contain the date range picker:
<input id="e1" name="e1" data-bind="datePicker: ''">
Finally, I added a subscribe to the holidayType observable which will call the enableDisableDateRange function any time holidayType changes:
this.holidayType.subscribe(function() {
this.enableDisableDateRange(this);
}, this);
Here is a fiddle: http://jsfiddle.net/6cfb1dLg/
NOTE: This all relies on how the date range picker arranges it's UI. Currently, it creates a <button> element directly after the <input> element it is applied to. If this changes, then the datePicker custom binding I wrote would have to be modified as well. The custom binding also assumes the presence of an observable named dateRangeButton and a function named enableDisableDateRange exist within the viewmodel.
I'm having a problem with this code:
$(function () {
$('#city_input').autocomplete({
onSelect: function (suggestion) {
alert(suggestion.data);
window.latlng = suggestion.data;
}
});
$('#fs_input').autocomplete({
params: { "latlng" : window.latlng, "latlng2" : '99.9999,-99.9999' },
onSelect: function (suggestion) {
alert(window.latlng);
}
});
});
This is an autocomplete script. First the user selects a suggestion on #city_input. Doing that, it sets the value for window.latlng.
Then next, the user needs to select a suggestion on #fs_input. The problem I'm having is that, in the "params" property, the "latlng" : window.latlng property is not being set. Just for tests, I also created another property, seen in the code, latlng2 which is working fine. Also, a little below, I'm doing an alert(window.latlng) to check the value and it alerts the expected value.
So the question is, why "latlng" : window.latlng is not being set? Maybe I'm not supposed to pass a variable to an object liberal property?
It is being set. It's simply that it has no value at the point when you're setting it.
The value isn't computed dynamically after #city_input's onSelect callback fires, it's being computed once when the script first loads, long before you ever assign to it.
$('#city_input').autocomplete({
onSelect: function (suggestion) {
alert(suggestion.data);
# you assign here, in a callback that executes some time after the page loads
window.latlng = suggestion.data;
}
});
$('#fs_input').autocomplete({
# however, you *read* the value here, immediately on page load, long before `onSelect` ever runs
# window.latlng is "undefined" at the point `params` is created.
params: { "latlng" : window.latlng, "latlng2" : '99.9999,-99.9999' },
onSelect: function (suggestion) {
alert(window.latlng);
}
});
If you want a value selected in the onSelect callback to be used in the autocomplete for a different element, you need to wait to initialize the autocomplete until after a value is selected and actually available:
$('#city_input').autocomplete({
onSelect: function (suggestion) {
alert(suggestion.data);
window.latlng = suggestion.data;
$('#fs_input').autocomplete({
params: { "latlng" : window.latlng, "latlng2" : '99.9999,-99.9999' },
onSelect: function (suggestion) {
alert(window.latlng);
}
});
}
});
In your code, I'm guessing that the onSelect function doesn't execute until a user has selected an item. However, the 'params' object is created when the autocomplete function is invoked, which is immediate (before a value has been assigned).
You need to initialise autocomplete on #fs_input only when you know the value of window.latlng (i.e. after the user has selected it):
$(function () {
$('#city_input').autocomplete({
onSelect: function (suggestion) {
alert(suggestion.data);
window.latlng = suggestion.data;
$('#fs_input').autocomplete({
params: { "latlng" : window.latlng, "latlng2" : '99.9999,-99.9999' },
onSelect: function (suggestion) {
alert(window.latlng);
}
});
}
});
});
I am not sure what cause it to fire three times after selecting a date through date calendar. Here is the options set for rangeSelector
rangeSelector:{
enabled:true,
inputEnabled: true,
inputDateFormat: '%d/%m/%Y',
inputEditDateFormat: '%d/%m/%Y',
inputDateParser: function (value) {
value = value.split('/');
console.log(value);
return Date.UTC(
parseInt(value[0]),
parseInt(value[1]) - 1,
parseInt(value[2])
);
}
}
by using backbone.view jquery selector, here how i initiate the chart
this.$el.highcharts(Options, this.extra);
and extra as additional settings to trigger the date picker
highlightSer: function (chart){
setTimeout(function () {
$('input.highcharts-range-selector', $(chart.container).parent())
.datepicker({
format: "dd/mm/yyyy",
todayBtn: "linked",
autoclose: true,
todayHighlight: true,
orientation: "auto right"
});
}, 0);
}
is anyone experience this?
#seeARMS solution from this SO worked for me, with one modification:
Create a separate function for your event
One function has the datePicker method and your settings:
$('input_with_date_picker').datePicker({...});
A separate function controls your on changeDate event:
$(document).on('changeDate', 'input_with_date_picker', function({..do something..}));
or:
$('input_with_date_picker').on('changeDate', function({..do something..}));
Update:
The fiddle you provided in the comments is working as expected because of your added function:
$('input.highcharts-range-selector').on('changeDate', function(e) {
alert(e.date);
});
On page load, it executes twice because you have two elements that match. They seem to both be generated by this JS:
rangeSelector:{
enabled:true,
inputEnabled:true,
inputDateFormat:"%d/%m",
inputEditDateFormat:"%d/%m/%Y",
inputDateParser: function (value) {
value = value.split('/');
console.log(value);
return Date.UTC(
parseInt(value[0]),
parseInt(value[1]) - 1,
parseInt(value[2])
);
}
},
If you change the date on either of the elements, the function executes once, which is the goal of your post.
You may be indicating it's not working because the dates aren't updating when you select a new date from the datePicker. I'm not sure why this is, I'm not familiar with highcharts.
I've set up a jsFiddle to demonstrate my problem.
What I'm doing:
I'm using Knockout with a jQuery Datepicker as shown in this question.
My model contains an observableArray which contains objects with date properties. Knockout renders an <input type="text"> for every object, and binds the value to the date using RP Niemeyer's datepicker binding.
What's going wrong:
If the user has a datepicker open when knockout's model changes (e.g., adding a new item), the datepicker stops working correctly. From what I can tell, the last <input type="text"> created while the datepicker is open becomes the new datepicker target. I need to fix the binding so that this does not happen.
Sample HTML:
<ul data-bind="foreach: dateBoxes">
<li>
<span data-bind="text: index"></span>
<input type="text" data-bind="datepicker: date"/>
</li>
</ul>
Sample Javascript:
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());
//handle date data coming via json from Microsoft
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);
}
}
};
var model;
var id = 0;
function DateBox(d) {
var self = this;
self.date = ko.observable(d);
self.index = ko.observable(id++);
}
function Model() {
var self = this;
self.dateBoxes = ko.observableArray([]);
self.changeCount = function() {
if (self.dateBoxes().length < 2) {
self.dateBoxes.push(new DateBox(new Date()));
} else if (self.dateBoxes().length > 1) {
self.dateBoxes.splice(0,1);
}
}
self.tick = function() {
self.changeCount();
setTimeout(function() {
self.tick();
}, 5000);
}
self.tick();
}
model = new Model();
ko.applyBindings(model);
Note: This answer isn't complete. However, as it's too large for a comment and probably helpful (towards a solution) anyhow I've taken the liberty and posted it as an answer. Anyone that can complete this answer is invited to do so through an edit or by spinning off into another better answer.
The thing that seems to be spoiling the party (taken from the documentation, emphasis mine):
This will be called once when the binding is first applied to an element, and again whenever the associated observable changes value.
If I hackisly prevent the update from doing its datepicker calls in the first update the datepicker won't break anymore (other issues do arise though). For the init I've added this line at the end:
element.isFirstRun = true;
Then the update method will do the following just before the datepicker calls:
if (element.isFirstRun) {
$(element).val(value);
element.IsFirstRun = false;
return;
}
See this updated fiddle for the results, which are:
the mentioned scenario now updates the correct textbox (a good thing);
the initial value is now a more verbose DateTime string and stays that way after updates (kinda not nice);
Hopefully this will help towards a more complete solution.
I'm trying to use KnockoutJS with jQuery UI. I have an input element with a datepicker attached. I'm currently running knockout.debug.1.2.1.js and it seems that the change event is never being caught by Knockout. The element looks like this:
<input type="text" class="date" data-bind="value: RedemptionExpiration"/>
I've even tried changing the valueUpdate event type but to no avail. It seems like Chrome causes a focus event just before it changes the value, but IE doesn't.
Is there some Knockout method that "rebinds all the bindings"? I technically only need the value changed before I send it back to the server. So I could live with that kind of workaround.
I think the problem's the datepicker's fault, but I can't figure out how to fix this.
Any ideas?
I think that for the jQuery UI datepicker it is preferable to use a custom binding that will read/write with Date objects using the APIs provided by the datepicker.
The binding might look like (from my answer here):
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
$el = $(element);
$el.datepicker(options);
//handle the field changing by registering datepicker's changeDate event
ko.utils.registerEventHandler(element, "changeDate", 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);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var 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() }" />
Sample in jsFiddle here: http://jsfiddle.net/rniemeyer/NAgNV/
Here is a version of RP Niemeyer's answer that will work with the knockout validation scripts found here: http://github.com/ericmbarnard/Knockout-Validation
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).val());
if (observable.isValid()) {
observable($(element).datepicker("getDate"));
$(element).blur();
}
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
ko.bindingHandlers.validationCore.init(element, valueAccessor, allBindingsAccessor);
},
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);
}
}
};
Changes are to the change event handler to pass the entered value and not the date to the validation scripts first, then only setting the date to the observable if it is valid. I also added the validationCore.init that is needed for custom bindings discussed here:
http://github.com/ericmbarnard/Knockout-Validation/issues/69
I also added rpenrose's suggestion for a blur on change to eliminate some pesky datepicker scenarios getting in the way of things.
I've used a different approach. Since knockout.js doesn't seem to fire the event on change, I've forced the datepicker to call change() for its input once closed.
$(".date").datepicker({
onClose: function() {
$(this).change(); // Forces re-validation
}
});
Although all of these answers saved me a lot of work, none of them fully worked for me. After selecting a date, the binded value would not update. I could only get it to update when changing the date value using the keyboard then clicking out of the input box. I fixed this by augmenting RP Niemeyer's code with syb's code to get:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
var funcOnSelectdate = function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
}
options.onSelect = funcOnSelectdate;
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", funcOnSelectdate);
//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 (typeof(value) === "string") { // JSON string from server
value = value.split("T")[0]; // Removes time
}
var current = $(element).datepicker("getDate");
if (value - current !== 0) {
var parsedDate = $.datepicker.parseDate('yy-mm-dd', value);
$(element).datepicker("setDate", parsedDate);
}
}
};
I suspect putting the observable($(element).datepicker("getDate")); statement in its own function and registering that with options.onSelect did the trick?
Thanks for this article I found it very useful.
If you want the DatePicker to behave exactly like the JQuery UI default behaviour I recommend adding a blur on the element in the change event handler:
i.e.
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
$(element).blur();
});
I solved this problem by changing the order of my included script files:
<script src="#Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
<script src="#Url.Content("~/Scripts/knockout-2.2.1.js")"></script>
Same as RP Niemeyer, but better support of WCF DateTime, Timezones and Using the DatePicker onSelect JQuery property.
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
var funcOnSelectdate = function () {
var observable = valueAccessor();
var d = $(element).datepicker("getDate");
var timeInTicks = d.getTime() + (-1 * (d.getTimezoneOffset() * 60 * 1000));
observable("/Date(" + timeInTicks + ")/");
}
options.onSelect = funcOnSelectdate;
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", funcOnSelectdate);
//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);
}
}
};
Enjoy :)
http://jsfiddle.net/yechezkelbr/nUdYH/
I think it can be done much easier: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />
So you do not need manual change handling in init function.
But in this case, your 'myDate' variable will get only visible value, not Date object.
Alternatively, you can specify this in binding:
Update:
function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("getDate");
if (typeof value === "string") {
var dateValue = new Date(value);
if (dateValue - current !== 0)
$(element).datepicker("setDate", dateValue);
}
}
Based on Ryan's solution, myDate returns the standard date string, which is not the ideal one in my case. I used date.js to parse the value so it will always return the date format you want. Take a look at this example fiddle Example.
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("getDate");
var d = Date.parse(value);
if (value - current !== 0) {
$(element).datepicker("setDate", d.toString("MM/dd/yyyy"));
}
}
I needed to repeatedly update my data from the server came across this but didn't quite finish the job for my needs sharing below(my date format /Date(1224043200000)/):
//Object Model
function Document(data) {
if (String(data.RedemptionExpiration).indexOf('/Date(') == 0) {
var newDate = new Date(parseInt(data.BDate.replace(/\/Date\((.*?)\)\//gi, "$1")));
data.RedemptionExpiration = (newDate.getMonth()+1) +
"/" + newDate.getDate() +
"/" + newDate.getFullYear();
}
this.RedemptionExpiration = ko.observable(data.RedemptionExpiration);
}
//View Model
function DocumentViewModel(){
///additional code removed
self.afterRenderLogic = function (elements) {
$("#documentsContainer .datepicker").each(function () {
$(this).datepicker();
});
};
}
After the model is formatted correctly for the output I added a template with the documentation knockoutjs:
<div id="documentsContainer">
<div data-bind="template: { name: 'document-template', foreach: documents, afterRender: afterRenderLogic }, visible: documents().length > 0"></div>
</div>
//Inline template
<script type="text/html" id="document-template">
<input data-bind="value: RedemptionExpiration" class="datepicker" />
</script>
Few people asked for dynamic datepicker options. In my case I needed a dynamic date range - so the first input defines the min value of the second and the second sets the max value for the first. I solved it by extending the RP Niemeyer's handler. So to his original:
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
$el = $(element);
$el.datepicker(options);
//handle the field changing by registering datepicker's changeDate event
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);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
I've added two more handlers corresponding to the options I wanted to modify:
ko.bindingHandlers.minDate = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("option", "minDate", value);
}
};
ko.bindingHandlers.maxDate = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("option", "maxDate", value);
}
};
And used them like that in my template:
<input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" />
<input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />
Using custom bindings provided in previous answers is not always possible. Calling $(element).datepicker(...) takes some considerable time, and if you have, for example, a few dozens, or even hundreds of elements to call this method with, you have to do it "lazy", i.e. on demand.
For example, the view model may be initialized, the inputs being inserted into the DOM, but the corresponding datepickers will only be initialized when a user clicks them.
So, here is my solution:
Add a custom binding that allows to attach arbitrary data to a node:
KO.bindingHandlers.boundData = {
init: function(element, __, allBindings) {
element.boundData = allBindings.get('boundData');
}
};
Use the binding to attcah the observable used for the input's value:
<input type='text' class='my-date-input'
data-bind='textInput: myObservable, boundData: myObservable' />
And finally, when initializing the datepicker, use it's onSelect option:
$('.my-date-input').datepicker({
onSelect: function(dateText) {
this.myObservable(dateText);
}
//Other options
});
This way, every time a user changes the date with the datepicker, the corresponding Knockout observable is also updated.