Setting datepicker with jQuery in CakePHP project

So, in this article I will explain how I create datepickers in my CakePHP projects with jQuery. So, let’s start:

1. You need to use jQuery lib 😉

2. Get this jQuery widget and put it in your /webroot/js folder. Don’t forget to include it in your layout.

(function( $ ) {
    $.widget( "ui.dp", {
            _create: function() {
                var el = this.element.hide();
                this.options.altField = el;
                var input = this.input = $('<input>').insertBefore( el )
                input.focusout(function(){
                        if(input.val() == ''){
                            el.val('');
                        }
                    });
                input.datepicker(this.options)
                if(convertDate(el.val()) != null){
                    this.input.datepicker('setDate', convertDate(el.val()));
                }
            },
            destroy: function() {
                this.input.remove();
                this.element.show();
                $.Widget.prototype.destroy.call( this );
            }
    });
   
    var convertDate = function(date){
      if(typeof(date) != 'undefined' && date != null && date != ''){
        return new Date(date);
      } else {
        return null;
      }
    }
})( jQuery );

What it does – the widget creates an extra field which is shown instead of the original one. A jQueryUI datepicker is attached to this newly created element and the original one is hidden and used as alternative field.

3. Add this code in your general script file.

$(document).ready(function() {
   $( "input.datepicker" ).dp({
      dateFormat: 'dd.mm.yy',
      altFormat: 'yy-mm-dd'
   });
});

This sets the date format in the datepicker to be for example “16.07.2011” while in the alt field will remain “2011-07-16” and this widget will be attached to all input fields which has .datepicker class (choose another if you like).

4. Change the type of the input to text.

echo $this->Form->input('start_date',
        array(
           'class'=>'datepicker',
           'type'=>'text'
        )
);

That’s it – no behaviors, no components, no extra helpers to handle the input. Another good point is that validations in the model are working without modification and the messages appear under the datepicker field.

In conclusion: I’ve wrote two posts about CakePHP dateformat and Datepicker long time ago an I think it deserve to get retired. 🙂

40 thoughts on “Setting datepicker with jQuery in CakePHP project

  1. toerti

    Hello Nic,

    this sounds really great. But it doesn´t work for me. Where does the datepicker class come from?
    Note that i am very new to CakePHP and usage of jquery 😉

  2. Nik Chankov Post author

    You need to set the class for the input which you want to have datepicker. For example:

    $this->Form->input(‘your_field’, array(‘class’=>’datepicker’));

    This will add a class datepicker to the field and the logic explained above should trigger.

  3. Nik Chankov Post author

    What do you mean is not defined? If you mean defined in CSS – it’s not required, this class is used just to trigger the jquery plugin, so all text fields with that class will be converted to datepicker fields.

  4. toerti

    Ah, okay – but i still don´t know what´s going wrong in my form.
    Here is what i´ve done.

    1. Put the first code above in datepicker.js in /webroot/js
    2. Add this line in the heas section of the layout: script(‘datepicker.js’); ?>
    3. Add the second code above to common.js in /webroot/js
    4. Add the field as text in my form this way: Form->input(‘day’, array(‘class’ => ‘datepicker’, ‘type’ => ‘text’)); ?>;

    Everything i get is a normal text field. 🙁

  5. Nik Chankov Post author

    Do you have jQuery library? Have you seen the Firebug console if it gives any JavaScript errors? Do you included jQuery UI as well because it’s required of course?

  6. Pierry Bos

    Hi.

    Yea, you can you really retired the others posts. I used JQuery’s plugin now your cake on 2.0 with success!

    I thought it would be a lot of work to do this in 2.0 version.

    Thank you very much.

    Ps.: For complete, your dp plugin have access to all datepicker attributes.
    I need use portuguese, and tryed:

    $(“input.datepicker”).dp({
    dateFormat: ‘dd.mm.yy’,
    altFormat: ‘yy-mm-dd’,
    dayNames: [“Domingo”,”Segunda”,”Terça”,”Quarta”,”Quinta”,”Sexta”,”Sábado”,”Domingo”],
    dayNamesMin: [“D”,”S”,”T”,”Q”,”Q”,”S”,”S”,”D”],
    dayNamesShort: [“Dom”,”Seg”,”Ter”,”Qua”,”Qui”,”Sex”,”Sáb”,”Dom”],
    monthNames: [“Janeiro”,”Fevereiro”,”Março”,”Abril”,”Maio”,”Junho”,”Julho”,”Agosto”,”Setembro”,”Outubro”,”Novembro”,”Dezembro”],
    monthNamesShort: [“Jan”,”Fev”,”Mar”,”Abr”,”Mai”,”Jun”,”Jul”,”Ago”,”Set”,”Out”,”Nov”,”Dez”],
    nextText: “Próximo”,
    prevText: “Anterior”
    });

    And… Sucess! A datePicker in PT-BR!

  7. Moses Liao

    Hi,

    I have some issues with your javascript. It has this error on the firebug:

    input.datepicker is not a function
    [Break On This Error] input.datepicker(this.options)

    What could be the problem?

  8. tadpole

    Awesome! Hats off. Why isn’t this a default part of datepicker?
    @Moses Liao, you need the core, ui, widgets and datepicker components for datepicker to work.

  9. tadpole

    Update: I’m not able to style the text box either with .datepicker or .hasDatepicker. Even setting attr via jquery does not work. How can I style the input text boxes?

  10. Wolli

    hey i did exactly the same like “toerti” … but soehow he didnt responded anymore … i included jQuery but still just a usual textfield. firebug says the following errors:

    $.widget is not a function — destroy: function() {

    $(“input.datepicker”).dp is not a function — altFormat: ‘yy-mm-dd’

    sry i am a complete dumbass when it comes to javascript and jquery …

  11. Nik Chankov Post author

    See if you follow the sequence:
    1. include jQuery lib
    2. include Datepicker.UI lib
    3. include my snippet from step 2 in the article (probably it’s better to put it into a separate file)
    4. Finally calling the step 3 from above (in your scripts file or in the header)
    5. Setting a class to the input field.

  12. Paca

    How can i pass the selected date to an ajax request? I wanna pick a date an get specific items from the db.

  13. Nik Chankov Post author

    Basically, you need to handle the event “onChange” and once the field value is changed, get it and use it as you want. But this should be another jQuery code.

  14. Sam

    This is awesome! Thanks a lot! And saves the effort of having to change CakePHP core or its automagic form elements.

  15. d-fens_

    hi, great article – tried to use jQuery-Timepicker-Addon (http://trentrichardson.com/examples/timepicker/) and changed input.datepicker(this.options) to input.datetimepicker(this.options) and added the js but i can’t get the time to show up in the field.
    using plain $(“input.datepicker”).datetimepicker(); works but something with the widget breaks this, you have any hint?

  16. sletis

    Hello nice tutorial !! i need also to add an Onchange Event in the hidden extra field in order to be able to calculate something ! how could i do that in Jquery?!! thanks!

  17. Nik Chankov Post author

    Hi, just listen for “change” event in the helper field. The easiest way will be:

    $('#your-field').prev().change(function(){
       var changedValue = $(this).next().val();
    })

    I haven’t tested it, but it should work.

  18. sletis

    Hey Nik, what are you calling Helper field? sorry i am very bad in Jquery i dont even understand where should i insert the code you sent to me :/

  19. sletis

    i inserted this code in my common.js

    $(‘date_deb’).prev().change(function(){
    alert(“Test Message” );
    })

    date_deb is my hidden field name so if i understand !
    when i change the datepicker alt field it s changing the hidden field value and then it should also print my “Test Message”.
    And it doesnt do it :/

    Does the Change function work with a real action?! like onchange in Javascript?

  20. Nik Chankov Post author

    First of all I hope “date_deb” is the field id, right? In that case it should be
    $(‘#date_deb’)
    Second this code should apply on the field which is originally meant to be datepicker. In other words lt’s say you have a field F1

    <input type="text" name="f1" id="f1" class="datepicker">

    if you apply datepicker on this field (by mentioninig the class) like in the example:

    $( "input.datepicker" ).dp({...});

    then for each field with datepicker class the script will create a field which will be shown instead of the original one (f1) and all values will be passed on the original one.
    Saying that your code should be something like:

    $(‘#f1’).prev().change(function(){
    alert(“Test Message” );
    });

    Dont forget to put this in document ready handler, otherwise it couldn’t work.

  21. Manny

    I’ve been banging my head trying to make it works .. sure I’m missing something simple due to my lack of expertise .. need some help here:
    This what I have done so far:
    – Downloaded JQuery and JQuery-ui.1.9.2.custom.js (complete) and placed both under app/webroot/js
    – Copied the code on item 2 in this post and created a new file MyDatePicker.js under app/webroot/js
    – Edited default.ctp under app/Views/Layouts adding this 3 lines
    Html->script(‘jquery’);
    echo $this->Html->script(‘jquery-ui-1.9.2.custom’);
    echo $this->Html->script(‘MyDatePicker’);
    …. // rest of existent code here
    ?>
    – In my app/View//edit.ctp I did add this line for my date field
    echo $this->Form->input(‘date_open’, array(
    ‘class’=>’datepicker’,
    ‘type’=>’text’
    ));

    – I didn’t include the code mentioned in point # 3 because basically I don’t have idea where or how to place it (and maybe this is the reason why this seems that is not working)
    – Run the app and I can see that Jquery, Jquery.ui, MyDatePicker js’s loaded and the field modified, but if I’m supposed to see a datapicker calendar to input in this field that is not working at all. I also see an JS error in the browser “Object no support this property or method MyDataPicker.js Line 3”.

    Could anybody help me a little to make it works? and understand what I’m doing wrong?
    thanks

  22. Manny

    scratch my previous post… got it working!! it was the not inclusion of code in point#3 the culprit… I just placed it on the default.ctp and voila.. working… tested OK in Chrome and guess what.. IE8 shows “NaN.NaN.NaN” in the formatted field instead the right date as Chrome does…. any explication/fix for that?

  23. Abiyeler

    Do you have jQuery library? Have you seen the Firebug console if it gives any JavaScript errors? Do you included jQuery UI as well because it’s required of course?

  24. Anderson Possamai

    Hello!
    Thank you for the code.
    I tried several modifications, validations Cake and only your solution worked.

    Just a detail, I do not know if it’s because I use time Brazilian, but for me, when editing the field, function convertDate() returned a day less. To solve this, I found the solution here using pregmatch: http://stackoverflow.com/a/2488358

    I also made a change in the line “input.datepicker(this.options),” adding the jquery maskedinput:
    input.datepicker(this.options).mask (’99/99/9999 ‘);

    Thank you, and I hope to have contributed.

  25. Mar

    Hey I got it to work with no problems, thx!

    However, Im having some trouble with a dinamically added input. It worked by adding, after the lines which add the input with datepicker class, these line:

    jQuery(‘.datepicker’).removeClass(‘hasDatepicker’).datepicker({ dateFormat: “dd/mm/yy”});

    However, now it either doesn’t trigger the datepicker or it shows both inputs (the one which should be hidden and the one with the datepicker).
    Any clue on the way I could add the functionality or trigger the widget dinamically?

  26. Nik Chankov Post author

    You need to run something like this while adding your new fields (assuming you do it with ajax call and the result is html)

    $.get(url, data, function(response){
      $(response).find('input.datepicker').each(function(){
        $(this).dp(...options...);
      });
    })

    The code in $(document).ready(function() {}) handles only the fields which are in the page while the document has been ready.

  27. Mar

    Actually Im not using ajax, so there’s no form reload. The new text inputs are added on an onClick function (thx for reply, though!)

  28. Mar

    Something to add:
    With $( document ).find(‘input.datepicker’).each(function(){
    $(this).dp({ dateFormat: “dd/mm/yy”});
    });
    it does trigger the datepickers but not the widget, so the first input is not hidden when it should.

  29. Martin

    Something wrong is happening to my site. Everything works fine, excepts that it shows a previous day than the one that it gets from the db.
    The value from the database is 2013-08-29 and the input shows me 28-08-2013
    Any ideas?
    Thanks in advance!

  30. Nik Chankov Post author

    What is the type of the date field? Is it DATE or DATETIME? If it’s the second option it could lead to wrong date. Check the source and see what is the date printed in the code. If it’s the correct one (2013-08-28) then the problem lays to the jQuery UI, if it’s (2013-08-29) then you don’t fetch the date properly.

  31. Paul

    You are the best, thank the good God some of you guys still believe in sharing such matter and content…it is guys like you that have made web development what is today!!! Thanks again!

  32. php nubi

    Im using cake 2.x and croogo 2.x
    On step 3: put this code in my “general script file”?
    ===
    $(document).ready(function() {
    $( “input.datepicker” ).dp({
    dateFormat: ‘dd.mm.yy’,
    altFormat: ‘yy-mm-dd’
    });
    });
    ===

    What do you mean ‘general script file’ ?
    Where should i put that code ?
    Sorry to ask, because im still learning about cake and croogo.

  33. Kaio

    I lost my mind trying to fix the dates problem with a diferent day. The problem is the function “convertDate” converts and add the time zone diferrence, resulting an incorrect date. If you don’t use time, change the function as follows:

    var convertDate = function(date){
    if(typeof(date) !== ‘undefined’ && date !== null && date !== ”){
    var dat = new Date(date);
    dat.setTime(dat.getTime() + (dat.getTimezoneOffset()*1000*60));
    return new Date(dat) ;
    } else {
    return null;
    }
    };

  34. Yinka

    Nice it works. Its fantastic. God bless you. Remember to include jquery file. Also include jquery UI JS and css file

Leave a Reply

Your email address will not be published. Required fields are marked *