10

I have two drop downs one for Hours and one for Minutes.I have managed to display the drop down for hoursenter image description here

But any one could assist - how do I display a drop-down for minutes,next to hours in th Ui Component form? like the one in the image.enter image description here

<field name="start_date">
    <argument name="data" xsi:type="array">
        <item name="options" xsi:type="object">NameSpace\ModuleName\Model\Xyz\Source\Hours</item>                
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Monday Opening Time</item>
            <item name="visible" xsi:type="boolean">true</item>
            <item name="dataType" xsi:type="string">number</item>
            <item name="formElement" xsi:type="string">select</item>
            <item name="source" xsi:type="string">item</item>
            <item name="dataScope" xsi:type="string">start_date</item>
            <item name="sortOrder" xsi:type="number">220</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
        </item>
    </argument>      
</field>

My Model returns the hours values

public function getHours()
{
    $availableOptions = [
        '0' => '00',
        '1' => '01',
        '2' => '02',
        '3' => '03',
        '4' => '04',
        '5' => '05',
        '6' => '06',
        '7' => '07',
        '8' => '08',
        '9' => '09',
        '10' => '10',
        '11' => '11',
        '12' => '12',
        '13' => '13',
        '14' => '14',
        '15' => '15',
        '16' => '16',
        '17' => '17',
        '18' => '18',
        '19' => '19',
        '20' => '20',
        '21' => '21',
        '22' => '22',
        '23' => '23',
    ];
    return $availableOptions;
}
3

Here's a way to do it by making a custom ui component. It requires the backing field to be a varchar.

Define the ui component in Your_Module/view/base/web/js/form/element/time.js:

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

    return AbstractElement.extend({
        defaults: {
            elementTmpl: 'Your_Module/form/time'
        },

        initialize: function () {
            this._super();

            this.hours = '00';
            this.minutes = '00';

            this.observe(['hours', 'minutes']);

            var value = this.value();

            this.hours(value.slice(0,2));
            this.minutes(value.slice(2));
        },

        userChanges: function () {
            this._super();

            this.value(this.hours() + this.minutes());
        },

        hoursOpts: (function () {
            var opts = [];

            for (var i=0; i<24; i++) {
                opts.push({
                    label: i.toString(),
                    value: ('0' + i).slice(-2)
                })
            }

            return opts;
        })(),

        minutesOpts: (function () {
            var opts = [];

            for (var i=0; i<60; i++) {
                opts.push({
                    label: ('0' + i).slice(-2),
                    value: ('0' + i).slice(-2)
                })
            }

            return opts;
        })()
    });
});

and the template, in Your_Module/view/base/web/template/form/time.html:

<select class="admin__control-select"
        data-bind="
        optgroup: hoursOpts,
        optionsValue: 'value',
        optionsText: 'label',
        value: hours,
        event: {change: userChanges}"/>

<select class="admin__control-select"
        data-bind="
        optgroup: minutesOpts,
        optionsValue: 'value',
        optionsText: 'label',
        value: minutes,
        event: {change: userChanges}"/>

Use it in your form xml like this:

<field name="start_date">
        <argument name="data" xsi:type="array">
             <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">Monday Opening Time</item>
                <item name="component" xsi:type="string">Your_Module/js/form/element/time</item>
                <item name="visible" xsi:type="boolean">true</item>
                <item name="dataType" xsi:type="string">text</item>
                <item name="formElement" xsi:type="string">input</item>
                <item name="source" xsi:type="string">item</item>
                <item name="dataScope" xsi:type="string">start_date</item>
                <item name="sortOrder" xsi:type="number">220</item>
                <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
          </item>
        </argument>      
    </field>

The value from the two drop downs is merged to produce a string like '0130' to represent the time 1:30, so your data type needs to be varchar otherwise the leading '0' will be dropped.

2
  • I followed what you said and works like a miracle :)
    – Verdu
    Feb 1 '17 at 5:11
  • how to use xml for yes/no items? Nov 12 '18 at 11:02
0

I use custom form element to achieve it.

namespace VendorName\ModuleName\Block\Widget\Form\Element;

use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\Data\Form\Element\Factory as ElementFactory;
use Magento\Framework\Data\Form\Element\CollectionFactory;
use Magento\Framework\Escaper;

/**
 * Class Time.
 */
class Time extends AbstractElement
{
    /**
     * Constructor.
     *
     * @param ElementFactory    $elementFactory
     * @param CollectionFactory $collectionFactory
     * @param Escaper           $escaper
     * @param array             $data
     */
    public function __construct(
        ElementFactory $elementFactory,
        CollectionFactory $collectionFactory,
        Escaper $escaper,
        array $data = []
    ) {
        parent::__construct($elementFactory, $collectionFactory, $escaper, $data);

        $this->setType('time');
    }

    /**
     * {@inheritdoc}
     */
    public function getElementHtml()
    {
        $this->addClass('select admin__control-select');

        $defaultValues = [
            'time'   => '00:00:00',
            'hour'   => '00',
            'minute' => '00',
            'second' => '00',
        ];

        $values = $this->getValue();
        if (!$values) {
            $values = $defaultValues;
        } else {
            $time = explode(':', $values);
            $values = [
                'time'   => $values,
                'hour'   => $time[0],
                'minute' => $time[1],
                'second' => $time[2],
            ];
        }

        // value container element
        $html = '<input type="hidden" id="' . $this->getHtmlId() . '" name="' . $this->getName()
            . '" value="' . $values['time'] . '" '. $this->_getUiId() . '/>' . PHP_EOL
        ;

        // hours control
        $html .= '<select style="width:80px" id="' . $this->getHourControlHtmlId() . '" '
            . $this->serialize($this->getControlHtmlAttributes()) . ' title="' . __('hours') . '" '
            . $this->_getUiId('hour') . '>' . PHP_EOL
        ;
        for ($i = 0; $i < 24; $i++) {
            $hour = str_pad($i, 2, '0', STR_PAD_LEFT);
            $html .= '<option'
                . ' value="' . $hour . '"'
                . ($values['hour'] == $i ? ' selected="selected"' : '') . '>'
                . $hour
                . '</option>' . PHP_EOL
            ;
        }
        $html .= '</select>' . PHP_EOL;

        // minutes control
        $html .= ':&nbsp;<select style="width:80px" id="' . $this->getMinuteControlHtmlId() . '" '
            . $this->serialize($this->getControlHtmlAttributes()) . ' title="' . __('minutes') . '" '
            . $this->_getUiId('minute') . '>' . PHP_EOL
        ;
        for ($i = 0; $i < 60; $i++) {
            $minute = str_pad($i, 2, '0', STR_PAD_LEFT);
            $html .= '<option'
                . ' value="' . $minute . '"'
                . ($values['minute'] == $i ? ' selected="selected"' : '') . '>'
                . $minute
                . '</option>' . PHP_EOL
            ;
        }
        $html .= '</select>' . PHP_EOL;

        // seconds control
        $html .= ':&nbsp;<select style="width:80px" id="' . $this->getSecondControlHtmlId() . '" '
            . $this->serialize($this->getControlHtmlAttributes()) . ' title="' . __('seconds') . '" '
            . $this->_getUiId('second') . '>' . PHP_EOL
        ;
        for ($i = 0; $i < 60; $i++) {
            $second = str_pad($i, 2, '0', STR_PAD_LEFT);
            $html .= '<option'
                . ' value="' . $second . '"'
                . ($values['hour'] == $i ? ' selected="selected"' : '') . '>'
                . $second
                . '</option>' . PHP_EOL
            ;
        }
        $html .= '</select>' . PHP_EOL;

        $html .= $this->getAfterElementHtml();

        $html .= $this->getAfterElementJs();

        return $html;
    }

    /**
     * Get after element JS.
     *
     * @return string
     */
    public function getAfterElementJs()
    {
        $js = '
            <script type="text/javascript">
                require(["jquery"], function ($) {
                    var onTimeContainerChange = function () {
                        var time      = $("#' . $this->getHtmlId() . '").val();
                        var timeArray = time.split(":");

                        $("#' . $this->getHourControlHtmlId() . '").val(timeArray[0]);
                        $("#' . $this->getMinuteControlHtmlId() . '").val(timeArray[1]);
                        $("#' . $this->getSecondControlHtmlId() . '").val(timeArray[2]);
                    };
                    $(document).ready(onTimeContainerChange);
                    $("#' . $this->getHtmlId() . '").change(onTimeContainerChange);

                    var onTimeControlChange = function () {
                        var time = $("#' . $this->getHourControlHtmlId() . '").val()
                            + ":" + $("#' . $this->getMinuteControlHtmlId() . '").val()
                            + ":" + $("#' . $this->getSecondControlHtmlId() . '").val()
                        ;

                        $("#' . $this->getHtmlId() . '").val(time);
                    }
                    $("'
                        . '#' . $this->getHourControlHtmlId() . ','
                        . '#' . $this->getMinuteControlHtmlId() . ','
                        . '#' . $this->getSecondControlHtmlId()
                    . '").change(onTimeControlChange);
                });
            </script>
        ';

        return $js;
    }

    /**
     * Get hour control html id prefix.
     *
     * @return string
     */
    protected function getHourControlHtmlId()
    {
        return $this->getHtmlId() . '_hour_control';
    }

    /**
     * Get minute control html id prefix.
     *
     * @return string
     */
    protected function getMinuteControlHtmlId()
    {
        return $this->getHtmlId() . '_minute_control';
    }

    /**
     * Get second control html id prefix.
     *
     * @return string
     */
    protected function getSecondControlHtmlId()
    {
        return $this->getHtmlId() . '_second_control';
    }

    /**
     * Get control html attributes.
     *
     * @return array
     */
    protected function getControlHtmlAttributes()
    {
        $propertiesToClear = ['title'];
        $htmlAttributes    = $this->getHtmlAttributes();

        return array_diff($htmlAttributes, $propertiesToClear);
    }
}

Customize it to fit your requirements, for example, removing the second field.

How to use:

use VendorName\ModuleName\Block\Widget\Form\Element\Time as TimeElement;

$form->addType('custom_time', TimeElement::class);
$form->addField(
    '[id_of_your_field]',
    'custom_time',
    [
        ... // data
    ]
);
3
  • Thanks @Rendy Eko Prastiyo , you did this using blocks- I am implementing via Ui Components .
    – Verdu
    Jan 31 '17 at 10:05
  • I don't think it's possible. Even Magento Catalog use form modifier to achieve inline mode of weight and product_has_weight columns on product edit backend. See Magento\Catalog\Ui\DataProvider\Product\Form\Modifier::customizeWeightField. Jan 31 '17 at 10:18
  • Well ! It seems , we ought to achieve it via block ; the way you did ! It would have been much better if we were able to use the time picker , which is mentioned in magento 2 docs , but the implementation is not available devdocs.magento.com/guides/v2.0/pattern-library/…
    – Verdu
    Jan 31 '17 at 10:42

This site is temporarily in read only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .