Yii 1.1: jQuery UI Datepicker

15 followers

We all love the convenient gadget, namely jQuery UI Datepicker as an input element for date. Yii supports it as CJuiDatePicker. Let's see how to use it.

Basics

Imagine we have a text field for date in our form.

<div class="row">
<?php echo $form->labelEx($model,'date_from'); ?>
<?php
echo $form->textField($model, 'date_from', array(
    'size' => 10,
    'maxlength' => 10,
));
?>
<?php echo $form->error($model,'date_from'); ?>
</div>

As you see, we are using a CActiveForm here. And now replace the text field with a CJuiDatePicker.

<div class="row">
<?php echo $form->labelEx($model,'date_from'); ?>
<?php
$this->widget('zii.widgets.jui.CJuiDatePicker', array(
    'model' => $model,
    'attribute' => 'date_from',
    'htmlOptions' => array(
        'size' => '10',         // textField size
        'maxlength' => '10',    // textField maxlength
    ),
));
?>
<?php echo $form->error($model,'date_from'); ?>
</div>

Basically this is all that you have to do.

  1. You don't need to load the javescript files (i.e. jQuery and jQuery UI) and CSS files by yourself.
  2. You don't need to write a text field by yourself.
  3. You don't need to write a script to convert the text field into Datepicker by yourself.

CJuiDatePicker will do all the tasks for you. (Well, things could be a little complicated in some situations. We will discuss it later.)

In the example above, we are using CJuiDatePicker like a CActiveForm::textField, setting $model to model and 'date_from' attribute of $model to attribute. By this settings, CJuiDatePicker will use the same logic as CActiveForm::textField to name the input field and connect the value to the attribute of the model.

If you don't use CActiveForm, you may use name and value to specify the name of the input field and the value of it. It correspond to the usage of CHtml::textField.

$this->widget('zii.widgets.jui.CJuiDatePicker', array(
    'name' => 'date_from',
    'value' => $fromDateValue,
    'htmlOptions' => array(
        'size' => '10',         // textField size
        'maxlength' => '10',    // textField maxlength
    ),
));

Note: You have to use either model-attribute pair or name-value pair. They are mutually exclusive. Do not mix them up.

Basic Datepicker

Localization

Now, let's localize the Datepicker.

$this->widget('zii.widgets.jui.CJuiDatePicker', array(
    'model' => $model,
    'attribute' => 'date_from',
    'language' => 'ja',
    // 'i18nScriptFile' => 'jquery.ui.datepicker-ja.js',
    'htmlOptions' => array(
        'size' => '10',
        'maxlength' => '10',
    ),
));

We have to specify 'language'. Generally you don't to need to specify 'i18nScriptFile' if you are using the latest version of jQuery UI.

Localized Datepicker

Theming

And we are applying a jQuery UI theme.

$this->widget('zii.widgets.jui.CJuiDatePicker', array(
    'model' => $model,
    'attribute' => 'date_from',
    'language' => 'ja',
    'themeUrl' => Yii::app()->baseUrl . '/css/jui',
    'theme' => 'softark',
    'cssFile' => 'jquery-ui-1.9.2.custom.css',
    'htmlOptions' => array(
        'size' => '10',
        'maxlength' => '10',
    ),
));

We are in the assumption that '/css/jui' holds a theme named 'softark'. You have to create a jQuery UI theme using ThemeRoller. It's not quite easy to create a good looking theme, though.

Themed Datepicker

Options

We can set various options.

$this->widget('zii.widgets.jui.CJuiDatePicker', array(
    'model' => $model,
    'attribute' => 'date_from',
    'language' => 'ja',
    'themeUrl' => Yii::app()->baseUrl . '/css/jui',
    'theme' => 'softark',
    'cssFile' => 'jquery-ui-1.9.2.custom.css',
    'options' => array(
        'showOn' => 'both',             // also opens with a button
        'dateFormat' => 'yy-mm-dd',     // format of "2012-12-25"
        'showOtherMonths' => true,      // show dates in other months
        'selectOtherMonths' => true,    // can seelect dates in other months
        'changeYear' => true,           // can change year
        'changeMonth' => true,          // can change month
        'yearRange' => '2000:2099',     // range of year
        'minDate' => '2000-01-01',      // minimum date
        'maxDate' => '2099-12-31',      // maximum date
        'showButtonPanel' => true,      // show button panel
    ),
    'htmlOptions' => array(
        'size' => '10',
        'maxlength' => '10',
    ),
));

The options are all up to your choice. Please see DatePicker DEMO and API for detailed information.

Note: My experience tells that the Datepicker works more stable without 'showAnim' option. Especially when you want to use 2 or more instances on the same screen, you would be better not set this option.

Datepicker with Options

Adjusting the appearance

But, we don't like the appearance of it at all. The header is divided into 2 rows because the dropdowns of year and month are a little too large. And we would like the font to be a little smaller.

Hmm, we have to tweak the CSS that ThemeRoller has created.

/* Component containers
----------------------------------*/
/* --- delete : use the default font of my site css ---
.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
*/
...
/* --- add : font should be a little smaller --- */
#ui-datepicker-div { font-size: 80%;}
button.ui-datepicker-trigger { font-size: 80%; line-height: 1.4em; }
...
 
/* --- layout of year and month ... have to modify a bit ---
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
*/
.ui-datepicker select.ui-datepicker-month { with: 35%; }
.ui-datepicker select.ui-datepicker-year { width: 40%; }
/* --- for fxxking stupid browsers --- */
* html .ui-datepicker .ui-datepicker-title select.ui-datepicker-month { width: 30%; }
* html .ui-datepicker .ui-datepicker-title select.ui-datepicker-year { width: 35%;}
/* --- highliting --- */
.ui-datepicker .ui-state-active, .ui-datepicker .ui-state-active.ui-state-highlight { border-color: #0b5110;}
.ui-datepicker .ui-state-highlight { border-color: #448844;}

So, the boring part of the task has been done. Of course you have to do your own job here. There's no standard way of doing it.

Datepicker

Holidays

I'm going to use GCalHolidays.js. It will enable you to color Sundays, Saturdays and Japanese Holidays with different colors.

Yes, I'm sorry, this part is Japanese specific. But you may find your locale specific counterpart somewhere on the web.

Yii::app()->cientScript->registerScriptFile(
    Yii::app()->baseUrl . '/js/GCalHolidays.js',
    CClientScript::POS_END
);

Datepicker with Holidays

Inside an AJAX updated area

In today's web applications, it's a common practice to update/replace the content of a specific area using AJAX. And if you want to use Datepicker inside such an area (e.g. in a form that will be ajax updated), you will need some extra work.

jQuery UI Datepicker modifies the existing HTML code to transform the ordinary text field into a Datepicker. So when the HTML code has been updated by an AJAX, the Datepicker will return to an plain text field.

You may encounter an issue like this: the Datepicker works just fine when the page has been loaded for the first time, but after you have done something in the page the Datepicker won't work anymore. Most probably the cause of the issue is that the HTML code has been updated by AJAX and the Datepicker has been reset to the plain text field.

To solve this kind of issue, we have to convert the plain text field to a Datepicker again after every ajax updating.

There are 2 ways to do that:

  1. Include a script to re-create the Datepicker in the AJAX response.
  2. Manually re-create the Datepicker after AJAX updating.

I don't have much to say about the 1st approach, because I've never used it. Probably you have to set the 4th parameter of Controller::renderPartial() to true to include the script in AJAX response. But, at the same time, you have to tweak CClientScript::scriptMap to avoid the duplication of jQuery and jQuery UI. ... I'm not sure. Personally I feel this approach is tricky, hacky and unstable. I don't like it.

I prefer the 2nd approach. I will not include any javascript in the response from the server. It goes like this:

$('#submit-button').click(function() {
    // post the form content by ajax
    $.ajax({
        'type' : 'POST',
        'url' : 'foo-update',
        'dataType' : 'json',
        'data' : $('#foo-form').serialize(),
        'success' : function(data){
            // update the form
            $('#foo-form').html(data.contents);
            // re-create Datepicker
            $('#foo-form .d-picker').datepicker();
        }
    });
});

It's just a rough skech, but the point is that we call datepicker() function of jQuery UI after the AJAX updating to re-create the Datepicker.

Yes, I'm doing a pure jQuery programming here. Although usually the jQuery support of Yii is quite convenient and useful, the things will be much easier with plain jQuery programming when the things got a little complicated.

But I will continue to use CJuiDatePicker for the initial loading of the Datepicker, because it's much more easy. And the following is a sample code to create a Datepicker when it has to be updated by AJAX.

$this->widget('zii.widgets.jui.CJuiDatePicker', array(
    ...
    'defaultOptions' => array(
        'showOn' => 'both',
        ...
        ... more options here ...
        ...
    ),
    'htmlOptions' => array(
        ...
        'class' => 'd-picker',
    ),
));

We set 'class' in 'htmlOptions' because we want to select this element using jQuery. Of course you may use 'id' instead.

And we use 'defaultOptions' instead of 'options'. By doing this, we will be able to call datepicker() function of jQuery UI without explicitly specifying the options.

Links

Total 11 comments

#17305 report it
keiny at 2014/05/21 08:14am
Holidays calendar

How to implement the calendar with holidays?

#17304 report it
keiny at 2014/05/21 08:14am
Holidays calendar

How to implement the calendar with holidays?

#15590 report it
Shahcheraghean at 2013/11/27 01:45am
Thank you

Nice work. So clear with examples.

#14877 report it
Gerhard Liebenberg at 2013/09/17 03:03pm
Excellent

Excellent work, Softark.

#14725 report it
Imre at 2013/09/06 04:46am
@Lynette

type=raw and value=html

and then either:

  • disable ajax (ajaxUpdate=>'false') in your CGridView or

  • write a custom datepicker reinitialisation script to turn your field into datepicker after each ajax update.

#14720 report it
Lynette at 2013/09/05 05:22pm
CGridView cell?

So, if I have a CGridView with a CHtml::textField that I'm using to capture a date, how do I turn CDataColumn into a datepicker?

#14261 report it
Azy at 2013/07/30 12:11pm
hi

it would be a nice enhancement if you can modify it to support multi select. tnx

#14258 report it
softark at 2013/07/30 07:53am
re: multiple date select

I see.

CJuiDatePicker is a wrapper over jQuery UI Datepicker. I think it doesn't support the MultiDatesPicker plug-in.

#14257 report it
Azy at 2013/07/30 07:19am
hi

Hi, this is what i am expecting.. http://multidatespickr.sourceforge.net/

to choose multiple at once...

#14253 report it
softark at 2013/07/30 06:28am
re: multiple date select

I'm not sure what you want exactly, but you can use multiple datepickers for multiple date fields on a single page, while you can not select multiple dates at once in a single datepicker.

#14245 report it
Azy at 2013/07/30 01:22am
multiple date select

is it possible to select multiple date select ?

Leave a comment

Please to leave your comment.

Write new article