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
        }
    });
});

Відповіді:


26

Спробуйте це ( Примітка . Не забудьте замінити рядок "Простір імен" та рядок "ModuleName" вашими значеннями):

<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 \ Модель \ Налаштування \ Джерело \ Параметри

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;
    }
}

app / code / Простір імен / ModuleName / view / adminhtml / web / js / form / element / 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-елемент не має onLoadметоду. Ви можете отримати будь-яке значення поля в будь-якому місці з реєстру з допомогою клавіші введення індексу: uiRegistry.get('index = field1'). Якщо у вас виникнуть додаткові запитання, будь ласка, зверніться до мене по скайпу (sarj1989), спілкуватися по-російськи буде простіше.
Сергій Учукхлебау

Дякую @Siarhey. Я вирішив використовувати ініціалізацію. this._super, ніж додайте необхідну перевірку.
жартаунік

1
Я не можу отримати значення поля, коли я використовую метод методу ініціалізації, значення "невизначене".
Саураб Талетія

1
@Siarhey Uchukhlebau Чи можу я замість цього додати прапорець?
Джуліано Варгас

9

Рішення, запропоноване 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);
        }
    });
});

Це працює належним чином.
Дахук Мітеш

+1 з мого боку. Жоден інший твір, окрім цього, не робив моєї роботи.
анонімний

7

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

Для всіх компонентів форми елемента ui, які розширюються, 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є також два аргументи (з додатковим 3-м).

  1. target- Це елемент, яким ви хочете маніпулювати в рамках цієї дії. Якщо ви не знайомі з тим, як імена елементів ui_component складаються в Magento, ви можете ознайомитися зі статтею Алана Шторма . Це в основному щось подібне {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це потрібно, воно буде потрібно лише тоді, коли його видно. Для цього не потрібно додаткової конфігурації.

Сподіваюся, що це допоможе для тих, хто шукає більш нестандартне рішення.


1
"Струни теж повинні працювати, але я не перевіряв цю теорію". Я випадково перевірений, і це не так ... Дії - це як масив правил, який потрібно починати з дії 0 або правила 0, а не 1, або рядка ...
Крістоф Ферребоуф

6

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

По-перше, звичайно, додайте свій власний компонент 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);
        }
    });
});

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. ваш голосуючий голос справляє неправильне враження.
Ronak Chauhan

@RonakChauhan - Домовилися про пункт !!! Ваша відповідь неправильна, різні люди мають різні думки, пропозиції та рішення. Ваша відповідь також правильна !!
Мантан Дейв

Чекати одну секунду для ініціалізації та блокувати ініціалізацію - це явно неправильний спосіб зробити це. Як ви навіть знаєте, що ваші залежності будуть завантажені за одну секунду? Чому не пройде дві секунди? Ви робите припущення, цього найкраще уникати.
Ерфан

Я не встановив тут 1 секунду, його в мілісекунді, SetTimeout () просто завантажить мій код після завантаження сторінки, і якщо у вас є відповідь, ви можете опублікувати його. Чиясь відповідь чимось відповідь - це не спосіб підтвердити себе! @Erfan
Ronak Chauhan

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;
        }
    });
});

це шоу "поле не визначене" після використання функції ініціалізації.
Принц Патель

1
Використання setTimeout()в fieldDepend()тому , що залежить ще не завантажений.
Ронак Чаухан

2

Існує декілька способів обробки залежностей від поля, для простого спаду "Так / Ні", прапорець або комутатор, ви можете використовувати властивості 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. Перевірте залежні поля у ui-компонентних формах у 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зв'язуванням для передачі значень спостережним або методам.

Для отримання додаткової інформації про властивості зв'язування ви можете перевірити властивості сполучення компонентів інтерфейсу


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))

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.