-1

Need time picker at ui form and ui grid inline editor magento 2.3.3

3 Answers3

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

0

You can create DatetimePicker class in yor module

class DatePicker extends \Magento\Config\Block\System\Config\Form\Field { /** * @var \Magento\Framework\Registry */ protected $_registry;

/**
 * @param \Magento\Backend\Block\Template\Context  $context
 * @param \Magento\Framework\Registry $coreRegistry
 * @param array    $data
 */
public function __construct(
    \Magento\Backend\Block\Template\Context $context,
    \Magento\Framework\Registry $registry,
    array $data = []
) {
    $this->_registry = $registry;
    parent::__construct($context, $data);
}

/**
 * @param \Magento\Framework\Data\Form\Element\AbstractElement $element
 * @return string
 */
protected function _getElementHtml(\Magento\Framework\Data\Form\Element\AbstractElement $element)
{
    $html = $element->getElementHtml();

    if (!$this->_registry->registry('datepicker_loaded')) {
        $this->_registry->registry('datepicker_loaded', 1);
    }

    $html .= '<button type="button" style="display:none;" class="ui-datepicker-trigger '
        .'v-middle"><span>Select Date</span></button>';

    $html .= '<script type="text/javascript">
        require(["jquery", "jquery/ui"], function (jq) {
            jq(document).ready(function () {
                jq("#' . $element->getHtmlId() . '").datepicker( { dateFormat: "yy/mm/dd" } );
                jq(".ui-datepicker-trigger").removeAttr("style");
                jq(".ui-datepicker-trigger").click(function(){
                    jq("#' . $element->getHtmlId() . '").focus();
                });
            });
        });
        </script>';

    return $html;
}

}

Kyle
  • 1