Reputation: 5153
I am trying to write a custom datepicker, where the default drop downs for months and year ranges (enabled through changeMonth
and changeYear
options) are replaced by custom drop downs. It is something like this:
Fiddle: http://jsfiddle.net/gGV3v/
$("#myId").datepicker({
...default settings...
beforeShow: function() {
...here I replace the default dropdowns by custom dropdowns...
...something like...
$(".ui-datepicker-month").replaceWith("#custom-html-block");
$(".ui-datepicker-year").replaceWith("#another-custom-html-block");
}
});
On choosing either month or year from the custom dropdowns, I want to change the date in the view accordingly. So I construct the date string from the current month and year (the date for the new month/year combo defaults to 1 here), and I call
$("#custom-html-block .custom-dropdown-option").on("click",function() {
...construct newDateString...
$("#myId").datepicker("setDate",newDateString)
});
$("#another-custom-html-block .custom-dropdown-option").on("click",function() {
...construct newDateString...
$("#myId").datepicker("setDate",newDateString)
});
I want the span
with text foo to remain intact when setting the new date programmatically on clicking it.
The problem is: it wipes off the custom drop downs and the default ones come again. I tried to do things like this:
$("#myId").datepicker($.extend({setDate: newDateString},oldSettings))
But it still doesn't work. How do I make this work?
Upvotes: 3
Views: 753
Reputation: 2202
You can set the value of the function $.datepicker._generateMonthYearHeader
, the downside it that will be global for all datepicker in the page.
The sample is below:
$.datepicker._generateMonthYearHeader = function(inst, drawMonth, drawYear, minDate, maxDate,
secondary, monthNames, monthNamesShort) {
var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
changeMonth = this._get(inst, "changeMonth"),
changeYear = this._get(inst, "changeYear"),
showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
html = "<div class='ui-datepicker-title'>",
monthHtml = "";
// year selection
if ( !inst.yearshtml ) {
inst.yearshtml = "";
if (secondary || !changeYear) {
html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
} else {
// determine range of years to display
years = this._get(inst, "yearRange").split(":");
thisYear = new Date().getFullYear();
determineYear = function(value) {
var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
(value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
parseInt(value, 10)));
return (isNaN(year) ? thisYear : year);
};
year = determineYear(years[0]);
endYear = Math.max(year, determineYear(years[1] || ""));
year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
inst.yearshtml += "<span class = 'dummy'>Foo</span> <select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>"
for (; year <= endYear; year++) {
inst.yearshtml += "<option value='" + year + "'" +
(year === drawYear ? " selected='selected'" : "") +
">" + year + "</option>";
}
inst.yearshtml += "</select>";
html += inst.yearshtml;
inst.yearshtml = null;
}
}
And your fiddle: http://jsfiddle.net/gGV3v/2/
UPDATE:
Without the buttons, and using a better approach, now you have control of all generated HTML:
var oldGenerateHTML = $.datepicker._generateHTML;
function newGenerateHTML(inst) {
var html = oldGenerateHTML.call(this, inst);
var $html = $(html);
$html.find('[data-handler=prev]').remove();
$html.find('[data-handler=next]').remove();
$html.find('[data-handler=selectMonth]').replaceWith('<span class="dummy">Foo</span>');
return $html;
}
$.datepicker._generateHTML = newGenerateHTML;
Fiddle also updated: http://jsfiddle.net/gGV3v/4/
Upvotes: 1
Reputation: 2262
In general this is why extend sucks. The parent (in your case datepicker) doesn't know anything about childs (your custom html). Everytime it refresh the view, your changes are lost. This problem is usually solved using composition. The goal is to create your own widget (I think that was the term in jQuery) that have datepicker as a local variable and to control _generateMonthYearHeader function calls. It's easy to say in theory but in practice (specially case with jQuery) is hard to achive. Easier solution will be to proxy the function.
//preserve _generateHTML because after it finish, html is visible and .ui-datepicker-month and .ui-datepicker-year are in dom tree
fn = $.datepicker._generateHTML; //preserve original function
$.datepicker._generateHTML = function(inst) {
//call original function
fn.call(this, inst);
//do custom changes
//you'll need better selectors in case of multiple datepicker instances
$(".ui-datepicker-month").replaceWith("#custom-html-block");
$(".ui-datepicker-year").replaceWith("#another-custom-html-block");
}
Personally I dislike this approach (I prefer composition as I said) but in your case it'll be easier to implement. The reason its easier is because generateMonthYearHeader is called from many different functions.
P.S. Not tested
Upvotes: 1