Buy

Creating a Date Picker Field

What's the ugliest part of the form? Yeah, we all know. It's this crazy 3 drop-downs used for the date field.

In modern times, if we need a date field, we're going to render it as a text field and use a fancy JavaScript widget to help the user fill it in.

Head back to the list of fields. It doesn't take long to find the one that's being guessed for the "First Discovered At" field: it's DateType.

Let's see if there's a way to render this as a text field instead.

Setting widget to single_text

Check out the widget option:

The basic way in which this field should be rendered.

It can be either choice - the three select fields, which is lame - 3 text fields, or a single text field with single_text. Ah hah!

Back in the form, let's pass in DateType::class even though we could be lazy and pass null. Create the array for the third argument and add widget set to single_text:

48 lines src/AppBundle/Form/GenusFormType.php
... lines 1 - 9
use Symfony\Component\Form\Extension\Core\Type\DateType;
... lines 11 - 13
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
... lines 19 - 34
->add('firstDiscoveredAt', DateType::class, [
'widget' => 'single_text'
])
;
}
... lines 40 - 46
}

Check it out.

HTML5 Date Types are Cool(ish)

Boom! It looks great. In fact, check this out: it already has some widget coolness: with a drop-down and a nice calendar.

Tip

If you don't see a fancy widget, don't worry! Not all browsers support this, which is why we'll use a true JavaScript widget soon!

This is not coming from Symfony: it's coming from my browser. Because as soon as we made this a single_text widget, Symfony rendered it as an <input type="date"> HTML5 field. Most browsers see this and add their own little date widget functionality.

Ready for the lame news? Not all browsers do this. And that means that users without this feature will have a pretty tough time trying to figure out what date format to pass.

Adding a JavaScript Date Picker

Instead, let's add a proper JavaScript widget. Google for "Bootstrap Date Picker". Ok, this first result looks pretty awesome - let's go for it!

First, we need to import new CSS and JS files. In new.html.twig, override the block stylesheets from the base layout. Add {% endblock %} and print {{ parent() }}:

35 lines app/Resources/views/admin/genus/new.html.twig
... lines 1 - 2
{% block stylesheets %}
{{ parent() }}
... lines 5 - 6
{% endblock %}
... lines 8 - 35

Because I'm lazy, I'll paste the URL to a CDN that hosts this CSS file:

35 lines app/Resources/views/admin/genus/new.html.twig
... lines 1 - 2
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/css/bootstrap-datepicker.css">
{% endblock %}
... lines 8 - 35

But, you can download it if you want.

Do the same thing for block javascripts. Add {% endblock %} and call parent():

35 lines app/Resources/views/admin/genus/new.html.twig
... lines 1 - 8
{% block javascripts %}
{{ parent() }}
... lines 11 - 18
{% endblock %}
... lines 20 - 35

I've got a CDN URL ready for the JavaScript file too. Go me!

35 lines app/Resources/views/admin/genus/new.html.twig
... lines 1 - 8
{% block javascripts %}
{{ parent() }}
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/js/bootstrap-datepicker.min.js"></script>
... lines 13 - 18
{% endblock %}
... lines 20 - 35

Adding a class Attribute

Next, how do we activate the plugin? According to their docs: it's pretty simple: select the input and call .datepicker() on it.

Personally, whenever I want to target an element in JavaScript, I give that element a class that starts with js- and use that with jQuery.

So the question is, how do we give this text field a class? You can't!

I mean you can! In 2 different ways! The first is by passing another option to the field in the form class. Add an attr option to an array. And give that array a class key set to js-datepicker:

50 lines src/AppBundle/Form/GenusFormType.php
... lines 1 - 13
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
... lines 19 - 34
->add('firstDiscoveredAt', DateType::class, [
... line 36
'attr' => ['class' => 'js-datepicker'],
... line 38
])
;
}
... lines 42 - 48
}

Setting up the JavaScript

Next, in our template, add the jQuery(document.ready) block. Hook it up with $('.js-datepicker').datepicker():

35 lines app/Resources/views/admin/genus/new.html.twig
... lines 1 - 8
{% block javascripts %}
{{ parent() }}
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/js/bootstrap-datepicker.min.js"></script>
<script>
jQuery(document).ready(function() {
$('.js-datepicker').datepicker();
});
</script>
{% endblock %}
... lines 20 - 35

Easy. Give it a try.

Scroll down and... hey! There it is! I can see the cool widget. And if I click... um... if I click... then - why is nothing happening?

HTML5 versus DatePicker: Fight!

It turns out, the HTML5 date functionality from my browser is fighting with the date picker. Silly kids. This doesn't happen in all browsers, it's actually something special to Chrome.

To fix this, we need to turn off the HTML5 date functionality. In other words, we want render this as a true <input type="text"> field, not a date field.

To do that, open the form type. There's one last option that will help us: set html5 to false:

50 lines src/AppBundle/Form/GenusFormType.php
... lines 1 - 13
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
... lines 19 - 34
->add('firstDiscoveredAt', DateType::class, [
... lines 36 - 37
'html5' => false,
])
;
}
... lines 42 - 48
}

Try it one last time. HTML5 is out of the way and the date picker is in charge.

Pretty awesome.

Leave a comment!

  • 2017-04-06 Diego Aguiar

    Hey Jelle!

    I'm guessing you forgot to import the CSS file of the datepicker library, if that's not the case, could yo show me how your script looks like ?

    Have a nice day!

  • 2017-04-06 Jelle Schouwstra

    Hi, my datepicker is blank, what could this mean?

  • 2017-03-13 weaverryan

    Yo Tony C!

    Interesting! Actually, Chrome *should* support this - http://caniuse.com/#feat=in... - but there are some other browser that don't. And even if they did, it might look a bit different :). We'll add a note!

    Thanks!

  • 2017-03-13 Tony C

    'widget' => 'single_text' "already has some widget coolness"
    Not so much. At first I though there was something seriously wrong with the template. Just Chrome.
    Perhaps you might note that in the lesson BEFORE you assume we're using a browser that will do it automagically?

  • 2017-02-17 weaverryan

    Yo Macarena Paz Diaz Colomes!

    It's a small thing probably! It should be jQuery - notice the capitalization - it's a little j, not a big J :). And $ and jQuery are the same thing - they both point to the exact same function.

    Cheers!

  • 2017-02-16 Macarena Paz Diaz Colomes

    Hi, It's me again.
    I've got the error "JQuery is not defined".

    The code is this:
    JQuery(document).ready(function(){
    $('.js-datepicker').datepicker();
    })
    If I replace the 'JQuery' with '$', it works. Do you know why?

    Thanks!

  • 2017-01-26 Victor Bocharsky

    Hey Michael,

    Hahaha, you're hooked! :) Unfortunately, it's impossible with `bootstrap-datepicker` JS library. Actually, they have an issue about including time in this widget, so you can track it here: https://github.com/uxsoluti... . Or just use another JS library which support date/time picking - there're a lot of such libs on GitHub.

    Cheers!

  • 2017-01-26 Michael

    Hi
    Sorry, its me again, but I can't stop watching your tutorials and learn symfony;-)
    Is there an easy way to adapt it as a date- and timepicker field with the default date and time of now?
    Cheers Michael