14

I want to show timepicker in my UiComponent form

As Magento Docs show the variations they provide timepicker : enter image description here

I want this using UiComponent in my form.

Note : Need to show schedule time so no date needed.

Checked reference :How to add time range picker to adminhtml form in Magento 2? (But its using block, I want using UiComponent)

4

3 Answers 3

11

You need to "create"* your own UI Component. You can do this by extending the Date UI Component.

#1 Add the XML to your form

Add the field to the fieldset. In this example, the component that we will be creating is called time. Note that you can declare a template in the following XML. However, it won't really do any good as it is the XHTML template that will wrap the Knockout template that does the actual rendering. There are other nodes you can declare here like validation.

<field name="time_field">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Time</item>
            <item name="dataType" xsi:type="string">text</item>
            <item name="formElement" xsi:type="string">date</item>
            <item name="dataScope" xsi:type="string">time_field</item>
            <item name="component" xsi:type="string">Your_Module/js/form/element/time</item>
        </item>
    </argument>
</field>

#2 Create the UI Component

// app/code/Your/Module/view/adminhtml/web/js/form/element/time.js

define([
    'Magento_Ui/js/form/element/date'
], function(Date) {
    'use strict';

    return Date.extend({
        defaults: {
            options: {
                showsDate: false,
                showsTime: true,
                timeOnly: true
            },

            elementTmpl: 'ui/form/element/date'
        }
    });
});

A few notes on the above Javascript:

elementTmpl is not necessary. However, if you want to customize the template (currently module-ui/view/base/web/templates/form/element/date.html), just create your own template and reference it with elementTmpl.

There are more options for the input. You can find more about them: http://trentrichardson.com/examples/timepicker/#tp-options. In the code, there is a list of all defaults here: /lib/web/jquery/jquery-ui-timepicker-addon.js

End Result:

time-component


  • At this point, I don't believe it is possible to declare your own UI Component in a way similar to that of definitions.xml. However, you can extend them with minimal effort. (And, if there is a way, please let me know).
1
  • 1
    @John, UI Components do not exist in Magento 1. But it is Magento <= 2.1. Magento 2.2 introduced some changes to the schema expectations. Perhaps I'll be able to add a note regarding that, but if you determine the Magento 2.2 schema, please post. Commented Apr 9, 2018 at 17:25
4

You can just use this xml code for the same result as above:

<field name="opening_time">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="dataType" xsi:type="string">string</item>
            <item name="label" xsi:type="string" translate="true">Opening time</item>
            <item name="formElement" xsi:type="string">date</item>
            <item name="source" xsi:type="string">page</item>
            <item name="sortOrder" xsi:type="number">70</item>
            <item name="dataScope" xsi:type="string">opening_time</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
            <item name="options" xsi:type="array">
                <item name="dateFormat" xsi:type="string">yyyy-MM-dd</item>
                <item name="timeFormat" xsi:type="string">HH:mm:ss</item>
                <item name="showsTime" xsi:type="boolean">true</item>
                <item name="timeOnly" xsi:type="boolean">true</item>
            </item>
            <item name="storeTimeZone" xsi:type="string">string</item>
        </item>
    </argument>
</field>

Result: enter image description here

0

I faced the same issue and I found out that the same issue was reported in the Magento 2 Github repository:

https://github.com/magento/magento2/issues/23157

And this is the Pull Request with the solution:

https://github.com/magento/magento2/pull/31549

It's fixed in Magento 2.4. So, i made a patch from that Pull Request to apply in previous Magento 2 versions with composer patches:

From: Santiago Valveson <https://www.linkedin.com/in/santiago-valveson/>
Subject: [PATCH] Invalid date when timeOnly defined on datetime UIComponent -
 Fixed in the v2.4

---
 view/base/web/js/form/element/date.js         |  9 +++-
 .../js/lib/knockout/bindings/datepicker.js    | 47 +++++++++----------
 2 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/view/base/web/js/form/element/date.js b/view/base/web/js/form/element/date.js
index 22fcdcb..a5447b2 100644
--- a/view/base/web/js/form/element/date.js
+++ b/view/base/web/js/form/element/date.js
@@ -107,6 +107,13 @@ define([
             return this._super().observe(['shiftedValue']);
         },

+        /**
+         * @inheritdoc
+         */
+        getPreview: function () {
+            return this.shiftedValue();
+        },
+
         /**
          * Prepares and sets date/time value that will be displayed
          * in the input field.
@@ -120,7 +127,7 @@ define([
                 if (this.options.showsTime && !this.options.timeOnly) {
                     shiftedValue = moment.tz(value, 'UTC').tz(this.storeTimeZone);
                 } else {
-                    shiftedValue = moment(value, this.outputDateFormat);
+                    shiftedValue = moment(value, this.outputDateFormat, true);
                 }

                 if (!shiftedValue.isValid()) {
diff --git a/view/base/web/js/lib/knockout/bindings/datepicker.js b/view/base/web/js/lib/knockout/bindings/datepicker.js
index 2fab8c2..ff09835 100644
--- a/view/base/web/js/lib/knockout/bindings/datepicker.js
+++ b/view/base/web/js/lib/knockout/bindings/datepicker.js
@@ -7,11 +7,8 @@ define([
     'ko',
     'underscore',
     'jquery',
-    'mage/translate',
-    'mage/calendar',
-    'moment',
-    'mageUtils'
-], function (ko, _, $, $t, calendar, moment, utils) {
+    'mage/translate'
+], function (ko, _, $, $t) {
     'use strict';

     var defaults = {
@@ -46,10 +43,12 @@ define([
                 observable = config;
             }

-            $(el).calendar(options);
+            require(['mage/calendar'], function () {
+                $(el).calendar(options);

-            ko.utils.registerEventHandler(el, 'change', function () {
-                observable(this.value);
+                ko.utils.registerEventHandler(el, 'change', function () {
+                    observable(this.value);
+                });
             });
         },

@@ -62,6 +61,7 @@ define([
          */
         update: function (element, valueAccessor) {
             var config = valueAccessor(),
+                $element = $(element),
                 observable,
                 options = {},
                 newVal;
@@ -75,26 +75,23 @@ define([
                 observable = config;
             }

-            if (_.isEmpty(observable())) {
-                if ($(element).datepicker('getDate')) {
-                    $(element).datepicker('setDate', null);
-                    $(element).blur();
+            require(['moment', 'mage/utils/misc', 'mage/calendar'], function (moment, utils) {
+                if (_.isEmpty(observable())) {
+                    newVal = null;
+                } else {
+                    newVal = moment(
+                        observable(),
+                        utils.convertToMomentFormat(
+                            options.dateFormat + (options.showsTime ? ' ' + options.timeFormat : '')
+                        )
+                    ).toDate();
                 }
-            } else {
-                newVal = moment(
-                    observable(),
-                    utils.convertToMomentFormat(
-                        options.dateFormat + (options.showsTime ? ' ' + options.timeFormat : '')
-                    )
-                ).toDate();

-                if ($(element).datepicker('getDate') == null ||
-                    newVal.valueOf() !== $(element).datepicker('getDate').valueOf()
-                ) {
-                    $(element).datepicker('setDate', newVal);
-                    $(element).blur();
+                if (!options.timeOnly) {
+                    $element.datepicker('setDate', newVal);
+                    $element.blur();
                 }
-            }
+            });
         }
     };
 });
--
2.21.0

This is the Magento 2 guide to apply composer patches:

https://devdocs.magento.com/guides/v2.3/comp-mgr/patching/composer.html

This is the code to make it work well (taken from the QA done by Magento to approve the Pull Request ):

<field name="start_date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">Start Time</item>
                    <item name="formElement" xsi:type="string">date</item>
                    <item name="source" xsi:type="string">import_profile</item>
                    <item name="dataScope" xsi:type="string">start_time</item>
                    <item name="options" xsi:type="array">
                    <item name="timeOnlyTitle" xsi:type="string">Select Start Time</item>
                    <item name="showsTime" xsi:type="boolean">true</item>
                    <item name="timeOnly" xsi:type="boolean">true</item>
                    <item name="timeFormat" xsi:type="string">HH:mm:ss</item>
                    <item name="dateFormat" xsi:type="string">yyyy-MM-dd</item>
                    </item>
                </item>
            </argument>
        </field>



Another solution (better to me) is use the type time input:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time

And create a new input template (I choose this way because i couldn't receive the time post param of the original date UI field.

in your module: Vendor/ModuleName/view/adminhtml/web/template/form/element/time_input.html

<input class="admin__control-text" type="time"
       data-bind="
        event: {change: userChanges},
        value: value,
        hasFocus: focused,
        valueUpdate: valueUpdate,
        attr: {
            name: inputName,
            placeholder: placeholder,
            'aria-describedby': noticeId,
            id: uid,
            disabled: disabled,
    }"/>

And then, in your form:

    <field name="start_date" formElement="input">
        <settings>
            <elementTmpl>Vendor_ModuleName/form/element/time_input</elementTmpl>
            <dataType>text</dataType>
            <dataScope>start_date</dataScope>
            <label translate="true">Start Date</label>
            <validation>
                <rule name="required-entry" xsi:type="boolean">true</rule>
            </validation>
        </settings>
    </field>

And you can standardize the post param in the Save/Validator Controller and the Data Provider with this:

date('H:i', strtotime($this->_request->getParam('opening_time')))

Should looks like this:

Input image

Input image with time select

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