Magento 2.1 Как создать пользовательское поле компонента формы, зависящее от другого значения поля?

13

У меня есть одно поле выбора, которое имеет несколько вариантов. Одно из них будет иметь некоторые поля в зависимости от значения, другое поле будет скрыто. Я скопировал и расширил компонент js для своего поля, но это не сработало или я сделал это неправильно. Ui компонент поддерживает эту функцию? Как мне этого добиться?

Ниже то, что я сделал:

<field name="field1">
    <argument name="data" xsi:type="array">
        <item name="options" xsi:type="object">Namespace\ModuleName\Model\Config\Source\Options</item>
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Field name</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">field1</item>
            <item name="component" xsi:type="string">Pathto/js/form/element/options</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
        </item>
    </argument>
</field>

<field name="field2Depend1"></field>
<field name="field3Depend1"></field>

jsComponent js/form/element/options:

define([
    'underscore',
    'uiRegistry',
    'Magento_Ui/js/form/element/select',
    'Magento_Ui/js/modal/modal'
], function (_, uiRegistry, select) {
    'use strict';

    return select.extend({

        onChange: function () {
            this.enableDisableFields();
        },

        /**
         * Enable/disable fields on Coupons tab
         */
        enableDisableFields: function () {
            // code check field
        }
    });
});
mrtuvn
источник

Ответы:

26

Попробуйте это ( Примечание : не забудьте заменить строку «Пространство имен» и строку «Имя модуля» на ваши значения):

<field name="field1">
    <argument name="data" xsi:type="array">
        <item name="options" xsi:type="object">Namespace\ModuleName\Model\Config\Source\Options</item>
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Parent Option</item>
            <item name="component" xsi:type="string">Namespace_ModuleName/js/form/element/options</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">field1</item>
            <item name="sortOrder" xsi:type="number">210</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
        </item>
    </argument>
</field>

<field name="field2Depend1">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string">Field 1</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="sortOrder" xsi:type="number">220</item>
            <item name="breakLine" xsi:type="boolean">true</item>
            <item name="visibleValue" xsi:type="string">2</item>
            <item name="visible" xsi:type="boolean">false</item>
        </item>
    </argument>
</field>
<field name="field3Depend1">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string">Field 2</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="sortOrder" xsi:type="number">230</item>
            <item name="breakLine" xsi:type="boolean">true</item>
            <item name="visibleValue" xsi:type="string">0</item>
            <item name="visible" xsi:type="boolean">false</item>
        </item>
    </argument>
</field>

Куда:

  • Видимость дочерних элементов по умолчанию установлена ​​как false ;
  • Значение visibleValue- это field1когда элемент должен быть видимым;

Пространство имен \ ModuleName \ Model \ Config \ Source \ Options

namespace Namespace\ModuleName\Model\Config\Source;

use Magento\Framework\Option\ArrayInterface;

class Options implements ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        $options = [
            0 => [
                'label' => 'Please select',
                'value' => 0
            ],
            1 => [
                'label' => 'Option 1',
                'value' => 1
            ],
            2  => [
                'label' => 'Option 2',
                'value' => 2
            ],
            3 => [
                'label' => 'Option 3',
                'value' => 3
            ],
        ];

        return $options;
    }
}

Приложение / код / ​​пространство имен / ModuleName / просмотр / adminhtml / веб / JS / форма / элемент / options.js

define([
    'underscore',
    'uiRegistry',
    'Magento_Ui/js/form/element/select',
    'Magento_Ui/js/modal/modal'
], function (_, uiRegistry, select, modal) {
    'use strict';

    return select.extend({

        /**
         * On value change handler.
         *
         * @param {String} value
         */
        onUpdate: function (value) {
            console.log('Selected Value: ' + value);

            var field1 = uiRegistry.get('index = field2Depend1');
            if (field1.visibleValue == value) {
                field1.show();
            } else {
                field1.hide();
            }

            var field2 = uiRegistry.get('index = field3Depend1');
            if (field2.visibleValue == value) {
                field2.show();
            } else {
                field2.hide();
            }

            return this._super();
        },
    });
});

Результат:

Значение 0 выбрано: Значение 0 выбрано

Значение 1 выбрано: Значение 1 выбрано

Значение 2 выбрано: Значение 2 выбрано

Значение 3 выбрано: Значение 3 выбрано

PS: Возможно, это не лучшее решение, но оно поможет вам

Сергей Учухлебау
источник
onUpdate работает хорошо, но как сделать onLoad? Как получить field1.value?
Жартауник
@zhartaunik Я думаю, вы должны использовать initializeметод в вашем случае, потому что у ui-element нет onLoadметода. Вы можете получить любое значение поля в любом месте из реестра с помощью клавиши ввода индекса: uiRegistry.get('index = field1'). Если у вас есть еще вопросы, пожалуйста, обращайтесь ко мне по скайпу (sarj1989), будет проще общаться на русском языке.
Сергей Учухлебау
Спасибо @Siarhey. Я решил использовать инициализацию. this._super, чем добавить необходимые проверки.
Жартауник
1
Я не могу получить значение поля, когда я использую метод инициализации, значение равно «undefined».
Саураб Талетия
1
@Siarhey Uchukhlebau Могу ли я добавить флажок вместо?
Джулиано Варгас
8

Решение, предложенное Magentix, время от времени выдает ошибку при использовании инициализации. Это зависит от времени, которое требуется вашему браузеру для рендеринга компонентов. Чтобы это исправить, вы можете использовать setTimeout.

Смотрите код ниже:

define([
    'underscore',
    'uiRegistry',
    'Magento_Ui/js/form/element/select',
    'Magento_Ui/js/modal/modal'
], function (_, uiRegistry, select, modal) {
    'use strict';

    return select.extend({

        /**
         * Extends instance with defaults, extends config with formatted values
         *     and options, and invokes initialize method of AbstractElement class.
         *     If instance's 'customEntry' property is set to true, calls 'initInput'
         */
        initialize: function () {
            this._super();

            this.resetVisibility();

            return this;
        },

        toggleVisibilityOnRender: function (visibility, time) {
            var field = uiRegistry.get('index = field_to_toggle');
            if(field !== undefined) {
                if(visibility == 1) {
                    field.show();
                } else {
                    field.hide();
                }

                return;
            }
            else {
                var self = this;
                setTimeout(function() {
                    self.toggleVisibilityOnRender(visibility, time);
                }, time);
            }
        },

        /**
         * On value change handler.
         *
         * @param {String} value
         */
        onUpdate: function (value) {
            if (value == 1) {
                this.showField();
            } else {
                this.hideField();
            }
            return this._super();
        },

        resetVisibility: function () {
            if (this.value() == 1) {
                this.showField();
            } else {
                this.hideField();
            }
        },

        showField: function () {
            this.toggleVisibilityOnRender(1, 1000);

        },

        hideField: function () {
            this.toggleVisibilityOnRender(0, 1000);
        }
    });
});
Mageinn
источник
Это работает правильно.
Дхадук Митеш
+1 с моей стороны. Ни одна из других работ, но это сделало мою работу.
Аноним
7

Это старый вопрос с несколькими ответами, которые работают, однако я нашел решение, использующее то, что предоставляет Magento (начиная с 2.1.0), без необходимости расширения компонентов. Поскольку несколько вопросов были помечены как дублирующие и направлены сюда, я подумал, что было бы полезно предоставить некоторую информацию по этому варианту.

Все расширяемые компоненты пользовательского интерфейса элемента формы Magento_Ui/js/form/element/abstract.jsимеют switcherConfigнастройку, доступную для таких целей, как скрытие / отображение элементов, а также другие действия. switcherКомпонент может быть найден в Magento_Ui / JS / форме / коммутатора для любопытных. Вы можете найти примеры его использования в sales_rule_form.xml и catalog_rule_form.xml . Конечно, если вы уже используете свой собственный пользовательский компонент, вы можете использовать его до тех пор, пока ваш компонент в конечном итоге расширяетсяabstract что, как представляется, основано на примере кода, приведенном в вопросе.

Теперь для более конкретного примера, чтобы ответить на оригинальный вопрос.

В Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmlвам просто нужно добавить следующие строки в поле х , settingsчто делает контрольный (то есть поле , которое определяет , какие поля скрыты / видны). В вашем примере это будет field1.

<field name="field1">
    <argument name="data" xsi:type="array">
        ...
    </argument>
    <settings>
        <switcherConfig>
            <rules>
                <rule name="0">
                    <value>2</value>
                    <actions>
                        <action name="0">
                            <target>your_entity_form.your_entity_form.entity_information.field2Depend1</target>
                            <callback>show</callback>
                        </action>
                        <action name="1">
                            <target>your_entity_form.your_entity_form.entity_information.field3Depend1</target>
                            <callback>hide</callback>
                        </action>
                    </actions>
                </rule>
                <rule name="1">
                    <value>3</value>
                    <actions>
                        <action name="0">
                            <target>your_entity_form.your_entity_form.entity_information.field2Depend1</target>
                            <callback>hide</callback>
                        </action>
                        <action name="1">
                            <target>your_entity_form.your_entity_form.entity_information.field3Depend1</target>
                            <callback>show</callback>
                        </action>
                    </actions>
                </rule>
            </rules>
            <enabled>true</enabled>
        </switcherConfig>
    </settings>
</field>

Давайте разберемся с этим немного. switcherКомпонент содержит массив , rulesкоторый является то , что мы строим здесь. У каждого <rule>есть имя, которое является числом в этом примере. Это имя является ключом / индексом массива для этого элемента. Мы используем числа в качестве индексов массивов. Струны тоже должны работать, но я не проверял эту теорию . ОБНОВЛЕНИЕ - Как упомянуто @ChristopheFerreboeuf в комментариях, здесь не работают строки. Это массивы и должны начинаться с 0, а не со строк или 1.

Внутри каждого ruleмы передаем два аргумента.

  1. value- это значение, field1которое должно сработать, как actionsуказано ниже.
  2. actions- Здесь у нас есть еще один массив. Это действия, которые должны запускаться при выполнении условий этого правила. Опять же, имя каждого action- это просто индекс / ключ массива этого элемента.

Теперь у каждого actionесть два аргумента (с необязательным третьим).

  1. target- Это элемент, которым вы хотите манипулировать в рамках этого действия. Если вы не знакомы с тем, как составляются имена элементов ui_component в Magento, вы можете ознакомиться со статьей Alan Storm . Это в основном что-то вроде {component_name}.{component_name}.{fieldset_name}.{field_name}этого примера.
  2. callback- Вот действия, которые необходимо предпринять по вышеупомянутому target. Этот обратный вызов должен быть функцией, доступной для целевого элемента. Наш пример использует hideи show. Здесь вы можете начать расширять доступную функциональность. catalog_rule_form.xmlПример , который я упоминал ранее , использует , setValidationесли вы хотите увидеть другой пример.
  3. Вы также можете добавить <params>к любому, actionчто призывает их. Вы можете увидеть это и в catalog_rule_form.xmlпримере.

Наконец, последний внутренний элемент switcherConfigявляется <enabled>true</enabled>. Это должно быть довольно просто, это логическое значение для включения / выключения функции коммутатора, которую мы только что реализовали.

И мы сделали. Таким образом , используя приведенный выше пример , что вы должны увидеть это поле field2Depend1отображается , если вы выбираете опцию со значением 2на field1, и field3Depend1отображается , если вы выбираете опцию со значением 3.

Я протестировал этот пример, используя только hideи showв обязательном поле, и он, похоже, учитывает видимость для проверки. Другими словами, если field2Depend1требуется, это потребуется только тогда, когда оно видно. Нет необходимости в дальнейшей настройке, чтобы это работало.

Надеюсь, что это поможет некоторым, кто ищет более готовое решение.

rain2o
источник
1
«Струны тоже должны работать, но я не проверял эту теорию». Я случайно проверил, и это не так ... Действия представляют собой массив правил, который должен начинаться с действия 0 или правила 0, а не 1 или строки ...
Кристоф Ферребу
6

Есть много ответов на этот вопрос, но большинство из них либо делают предположения о том, полностью ли загружен uiRegistry, либо используют setTimeout для очистки стека вызовов, и ждут следующего события EventLop (что, на мой взгляд, все еще является неправильным способом сделайте это - так как вы не можете быть уверены, когда загрузились другие компоненты пользовательского интерфейса - поправьте меня, если я ошибаюсь).

Во-первых, конечно, добавьте свой пользовательский компонент JS в конфигурацию поля (подробности см. В других ответах):

<item name="component" xsi:type="string">Namespace_ModuleName/js/form/element/options</item>

Затем, вот пользовательский компонент пользовательского интерфейса, который скрывает или показывает зависимые поля - с комментариями, чтобы объяснить, что происходит.

define([
    'underscore',
    'uiRegistry',
    'Magento_Ui/js/form/element/select'
], function (_, uiRegistry, select) {

    'use strict';

    return select.extend({

        /**
         * Array of field names that depend on the value of 
         * this UI component.
         */
        dependentFieldNames: [
            'my_field_name1',
            'my_field_name2'
        ],

        /**
         * Reference storage for dependent fields. We're caching this
         * because we don't want to query the UI registry so often.
         */
        dependentFields : [],

        /**
         * Initialize field component, and store a reference to the dependent fields.
         */
        initialize: function() {
            this._super();

            // We're creating a promise that resolves when we're sure that all our dependent
            // UI components have been loaded. We're also binding our callback because
            // we're making use of `this`
            uiRegistry.promise(this.dependentFieldNames).done(_.bind(function() {

                // Let's store the arguments (the UI Components we queried for) in our object
                this.dependentFields = arguments;

                // Set the initial visibility of our fields.
                this.processDependentFieldVisibility(parseInt(this.initialValue));
            }, this));
        },

        /**
         * On value change handler.
         *
         * @param {String} value
         */
        onUpdate: function (value) {
            // We're calling parseInt, because in JS "0" evaluates to True
            this.processDependentFieldVisibility(parseInt(value));
            return this._super();
        },

        /**
         * Shows or hides dependent fields.
         *
         * @param visibility
         */
        processDependentFieldVisibility: function (visibility) {
            var method = 'hide';
            if (visibility) {
                method = 'show';
            }

            // Underscore's invoke, calls the passed method on all the objects in our array
            _.invoke(this.dependentFields, method);
        }
    });
});
Erfan
источник
5

Если вы получили ошибку, как Field is Undefinedпри инициализации видимости полей, используйте setTimeout()для загрузки зависимых полей:

fieldDepend: function (value) {
     setTimeout(function(){ 
        var field1 = uiRegistry.get('index = field2');

        if (field1.visibleValue == value) {
               field1.show();
        } else {
               field1.hide();
        }

       var field2 = uiRegistry.get('index = field3');

        if (field2.visibleValue == value) {
              field2.show();
        } else {
              field2.hide();
        }    
     }, 1);
     return this._super();
},
Ронак Чаухан
источник
Вместо setTimeout используйте асинхронный метод получения зависимостей:uiRegistry.get('q', function(field) { ... }));
Erfan
Вместо того, чтобы предлагать в комментарии и переоценить мой ответ, вы можете опубликовать здесь свой ответ, братан, Это не способ посвятить какой-либо ответ, Вы просто предлагаете другой способ, мой ответ не является неправильным. @Erfan. ваш отрицательный голос производит неправильное впечатление.
Ронак Чаухан
@RonakChauhan - Договорились о точке! Ваш ответ не является правильным, разные люди имеют разные мнения, предложения и решения. Ваш ответ тоже правильный !!
Мантан Дейв
Ожидание инициализации в течение одной секунды и блокировка инициализации - это явно неправильный способ сделать это. Откуда вы знаете, что ваши зависимости будут загружены за одну секунду? Почему не будет две секунды? Вы делаете предположение здесь, этого лучше избегать.
Эрфан
Я не установил здесь 1 секунду, в миллисекундах SetTimeout () просто загрузит мой код после загрузки страницы, и если у вас есть ответ, вы можете опубликовать его. Голосовать против кого-то - это не способ доказать свою правоту! @Erfan
Ронак Чаухан,
2

Пользовательский компонент с init:

define([
    'underscore',
    'uiRegistry',
    'Magento_Ui/js/form/element/select',
    'Magento_Ui/js/modal/modal'
], function (_, uiRegistry, select, modal) {
    'use strict';

    return select.extend({

        /**
         * Init
         */
        initialize: function () {
            this._super();

            this.fieldDepend(this.value());

            return this;
        },

        /**
         * On value change handler.
         *
         * @param {String} value
         */
        onUpdate: function (value) {
            this.fieldDepend(value);

            return this._super();
        },

        /**
         * Update field dependency
         *
         * @param {String} value
         */
        fieldDepend: function (value) {
            var field = uiRegistry.get('index = field_to_toggle');

            if (value == 'xxxxx') {
                field.show();
            } else {
                field.hide();
            }

            return this;
        }
    });
});
Magentix
источник
это показывает "поле не определено" после использования функции инициализации.
Принц Патель
1
Используйте setTimeout()в fieldDepend()потому что Зависимый еще не загружен.
Ронак Чаухан
1

На всякий случай, если кто-то борется с решением Erfan , вы должны передать полный путь к полям dependentFieldNames, например:

       dependentFieldNames: [
        'form_name.form_name.fieldset.field_name',
        'form_name.form_name.fieldset.field_name1',
        'form_name.form_name.fieldset.field_name2',
        'form_name.form_name.fieldset.field_name3'
    ],

Я не уверен, почему form_name должно быть 2 раза, но это сработало для меня.

Для отладки это я положил console.log(query);в static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.js223 - м линии (ГЭТ () функции непосредственно перед this._addRequest(query, callback))

user3722573
источник
1

Существует несколько способов обработки полевых зависимостей: для простого раскрывающегося списка «Да / Нет», флажка или переключателя можно использовать importsили exportsсвязать свойства в Magento 2. Решение подробно описано здесь: Зависимые поля в формах компонентов пользовательского интерфейса в Magento 2 без Javascript для логических полей :

<!-- In the parent field <settings>...</settings> -->
<exports>
    <link name="checked">${$.parentName}.description:disabled</link>
</exports>

<!-- or -->

<!-- In the dependent field <settings>...</settings> -->
<imports>
    <link name="disabled">${$.parentName}.is_active:checked</link>
</imports>

Для обработки значений другого типа, таких как зависимость от списка значений в раскрывающемся списке или, хотя маловероятно, значение поля ввода, вы можете использовать switcherConfig. Проверьте зависимые поля в формах компонентов пользовательского интерфейса в Magento 2 без Javascript для информации.

<switcherConfig>
    <rules>
        <rule name="0">
            <value>list</value><!-- Actions defined will be trigger when the current selected field value matches the value defined here-->
            <actions>
                <action name="0">
                    <target>hs_xml_dependentfield_model_form.hs_xml_dependentfield_model_form.general.list</target>
                    <callback>visible</callback>
                    <params>
                        <param name="0" xsi:type="boolean">true</param>
                    </params>
                </action>
                <action name="1">
                    <target>hs_xml_dependentfield_model_form.hs_xml_dependentfield_model_form.general.hex_code</target>
                    <callback>visible</callback>
                    <params>
                        <param name="0" xsi:type="boolean">true</param>
                    </params>
                </action>
            </actions>
        </rule>
        ...
    </rules>
    <enabled>true</enabled>
</switcherConfig>

Приведенные выше 2 правила обрабатывают практически все, используя конфигурацию XML. Для более сложных правил вы также можете использовать JavaScript.

Каждое поле в форме компонента пользовательского интерфейса является компонентом, который можно расширить с помощью componentатрибута для <field component="path to your js" ...>...</field>. Затем вы можете использовать это поле data.configдля передачи дополнительной информации компоненту, если компонент является универсальным и повторно используется в нескольких местах в сочетании с importsилиexports свойством linking для передачи значений наблюдаемым или методам.

Для получения дополнительной информации о свойствах связывания вы можете проверить свойства связывания компонентов пользовательского интерфейса.

hungersoft
источник