3

I would like to impliment the native craft datepicker ui in my custom fieldType. From looking at the native date template (craft/app/templates/_includes/form/date.html) I can see that it inserts javascript using includeJs, which I have followed exactly. Here is my template code:

<div class="input">
    <div class="datewrapper">
        <input type="text" autocomplete="off" value="" size="10" id="trial_end" data-stripe="trial_end" class="text hasDatepicker">
     </div>
</div>

{%- set js %}
    var $datePicker = $('#{{ 'trial_end'|namespaceInputId|e('js') }}');
    $datePicker.datepicker({
        constrainInput: false,
        dateFormat: '{{ craft.i18n.datepickerJsFormat }}',
        defaultDate: new Date(),
        prevText:   '{{ "Prev"|t|e('js') }}',
        nextText:   '{{ "Next"|t|e('js') }}',
    });
{%- endset %}
{%- includeJs js %}

I have confirmed that the js output is identical to the other native date fields, and that the namespaced id matches between the js selector and the html input field id. But no datepicker. Is there a piece missing?

Update: Perhaps my question needs some clarification. My field type includes additional 'dummy' fields that will never get stored in the database, but rather are only used to collect information from the user that will be sent via ajax to a 3rd party service, with the result customerId being stored in the database. One of those dummy fields is a date field that I would like to use the native datepicker for. Here is an image:

This is the custom field

Yes. That is all one field! that only stores a customerid from a third party service (Stripe), and loads all of the customer account info via ajax. The actual field 'customerID' is never exposed to the client, as it generated automatically.

  • I have also tried to delay the instantiation of the datepicker using .setTimeout(), instantiate on demand using $body.on('click', $elmt, function(e){...});, and force it on with $datepicker.show();. But no luck — at a total loss. – Douglas McDonald Oct 17 '14 at 0:16
5
+50

Here’s what your field type’s getInputHtml() method should look like:

public function getInputHtml($name, $value)
{
    // If $value['trial_end'] is set, make sure that it's a DateTime object
    if (!empty($value['trial_end']))
    {
        $value['trial_end'] = DateTime::createFromString($value['trial_end']);
    }
    else
    {
        $value['trial_end'] = '';
    }

    return craft()->templates->render('myplugin/input', array(
        'id'    => craft()->templates->formatInputId($name),
        'name'  => $name,
        'value' => $value,
    ));
}

Then your plugin’s input.html template would be responsible for showing the Datepicker input.

The easiest way to create a Datepicker input is to either include the _includes/forms/date.html template directly into your template, or use the date() macro from _includes/forms.html.

{% from "_includes/forms" import date %}

{{ date({
    id: id ~ '-trial_end',
    name: name ~ '[trial_end]',
    value: value.trial_end
}) }}

However if you need to tweak the HTML, such as adding that ata-stripe="trial_end" attribute, you can just copy the HTML from the _includes/forms/date.html template directly into yours.

In your case, it’s probably not working because you have that hasDatepicker class on your <input>, which tells the Datepicker JS that a datepicker instance has already been assigned to that input, so the new datepicker initialization is getting skipped. Remove that class and you should be good to go.

When an entry is saved, you will need to convert the user-submitted date value into a DateTime object, which you can do using DateTime::createFromString().

On a normal field type you would want to put that code within prepValueFromPost(), like this:

public function prepValueFromPost($value)
{
    // Convert trial_end to a DateTime object
    $value['trial_end'] = DateTime::createFromString($value['trial_end']);

    return $value;
}

However in your case, if your field type isn’t storing any data and its defineAttributeConfig() method is returning false, a better place to put it would be in onAfterElementSave():

public function onAfterElementSave()
{
    // Get the raw POST value for this field
    $value = $this->element->getContent()->getAttribute($this->model->handle);

    // Convert trial_end to a DateTime object
    $value['trial_end'] = DateTime::createFromString($value['trial_end']);

    // ...
}

If you’re looking for an example third party field type that works with dates, check out our Store Hours plugin. It’s using timepickers rather than datepickers, but those both work exactly the same.

  • Thanks Brandon! I wish it were that simple. Unfortunately, I am not actually creating a real date field that is stored in the database but rather an input field, that just borrows the UI of a craft date field. I updated my question to clarify. – Douglas McDonald Oct 22 '14 at 18:10
  • @DouglasMcDonald That’s fine - the majority of the answer still applies. Just added a little more detail to the end for your use case. – Brandon Kelly Oct 22 '14 at 18:21
  • I tried using the date() macro and it works perfectly. However, it doesn't allow me to add a custom attribute data-stripe="trialEnd". What I don't understand is that I am replicating the functionality of the '_includes/forms/date.html' inline, but the datepicker does not seem to work. There is some small piece missing to the puzzle somewhere. Is the date field instantiated in some way before the template gets rendered? – Douglas McDonald Oct 22 '14 at 18:44
  • Looking at the actual source output, the two fields (custom vs using date() macro) next to each other look identical (minus the extra attribute). And the javascript var $datePicker=... includes at the bottom are identical. – Douglas McDonald Oct 22 '14 at 18:53
  • 1
    Perfect! You're a genius! Problem solved. Thank you. Check out the cool stuff we can do with Craft ;) – Douglas McDonald Oct 22 '14 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.