Magento 2.1 Jak utworzyć niestandardowe pole komponentu formularza zależne od innej wartości pola?

13

Mam jedno pole, które ma kilka opcji. Jedno z nich będzie miało pola zależne od wartości, inne będą ukryte. Skopiowałem i rozszerzyłem js komponentu dla mojego pola, ale to nie działało lub zrobiłem to źle. Składnik interfejsu użytkownika obsługuje tę funkcję? Jak mogę to osiągnąć?

Poniżej zrobiłem:

<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
źródło

Odpowiedzi:

26

Spróbuj tego ( Uwaga : nie zapomnij zastąpić wiersza „Namespace” i wiersza „ModuleName” własnymi wartościami):

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

Gdzie:

  • Widoczność elementów potomnych jest domyślnie ustawiona na false;
  • visibleValue- jest field1to wartość, gdy składnik powinny być widoczne;

Przestrzeń nazw \ Nazwa modułu \ Model \ Konfig \ Źródło \ Opcje

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 / Namespace / 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();
        },
    });
});

Wynik:

Wybrano wartość 0: Wybrano wartość 0

Wybrano wartość 1: Wybrano wartość 1

Wybrano wartość 2: Wybrano wartość 2

Wybrano wartość 3: Wybrano wartość 3

PS: Być może nie jest to najlepsze rozwiązanie, ale powinno ci pomóc

Siarhey Uchukhlebau
źródło
onUpdate działa dobrze, ale jak zrobić onLoad? Jak zdobyć field1.value?
zhartaunik
@zhartaunik Myślę, że powinieneś użyć initializemetody w twoim przypadku, ponieważ element interfejsu użytkownika nie ma onLoadmetody. Można dostać żadnej wartości pola w dowolnym miejscu z rejestru przy użyciu klucza indeksu wejściowego: uiRegistry.get('index = field1'). W przypadku dodatkowych pytań proszę o kontakt w skype (sarj1989). Łatwiej będzie komunikować się w języku rosyjskim.
Siarhey Uchukhlebau,
Dzięki @Siarhey. Zdecydowałem się użyć inicjalizacji. this._super, niż dodaj niezbędną weryfikację.
zhartaunik,
1
Nie mogę uzyskać wartości pola, gdy korzystam z metody inicjalizacji, wartość jest „niezdefiniowana”.
Saurabh Taletiya
1
@Siarhey Uchukhlebau Czy mogę zamiast tego dodać pole wyboru?
Juliano Vargas,
9

Rozwiązanie sugerowane przez Magentix od czasu do czasu generuje błąd podczas inicjalizacji. Zależy to od czasu renderowania komponentów przez przeglądarkę. Aby to naprawić, możesz użyć setTimeout.

Zobacz poniższy kod:

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
źródło
Działa poprawnie.
Dhaduk Mitesh,
+1 z mojej strony. Żadna z innych prac, ale to nie spełniło mojej roli.
anonimowy
7

To stare pytanie z wieloma odpowiedziami, które działają, ale znalazłem rozwiązanie wykorzystujące to, co zapewnia Magento (od wersji 2.1.0), bez potrzeby rozszerzania komponentów. Ponieważ wiele pytań zostało oznaczonych jako duplikaty i skierowanych tutaj, pomyślałem, że dobrze byłoby podać informacje na temat tej opcji.

Wszystkie rozszerzane elementy interfejsu użytkownika elementu formularza Magento_Ui/js/form/element/abstract.jsmają switcherConfigustawienie dostępne do takich celów, jak ukrywanie / wyświetlanie elementów, a także innych działań. switcherSkładnik można znaleźć w Magento_Ui / js / / postać przełącznika do ciekawy. Przykłady tego można znaleźć w pliku sales_rule_form.xml i catalog_rule_form.xml . Oczywiście, jeśli używasz już własnego niestandardowego komponentu, możesz go nadal używać, dopóki komponent ostatecznie się rozszerzy, abstractco wydaje się być przypadkiem na podstawie przykładowego kodu podanego w pytaniu.

Teraz bardziej konkretny przykład odpowiedzi na pierwotne pytanie.

W Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmlwystarczy po prostu dodać do pola, settingsktóre kontroluje (tj. Pole, które określa, które pola są ukryte / widoczne). W twoim przykładzie byłoby to 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>

Rozwalmy to trochę. switcherSkładnik zawiera tablicę rules, która jest co budujemy tutaj. Każdy <rule>ma nazwę, która jest liczbą w tym przykładzie. Ta nazwa to klucz / indeks tablicy dla tego elementu. Używamy liczb jako indeksów tablic. Ciągi też powinny działać, ale nie przetestowałem tej teorii . AKTUALIZACJA - Jak wspomniano w @ChristopheFerreboeuf w komentarzach, ciągi znaków nie działają tutaj. Są to tablice i powinny zaczynać się od 0, a nie ciągów znaków lub 1.

Wewnątrz każdego ruleprzekazujemy dwa argumenty.

  1. value- Jest to wartość, field1która powinna uruchomić actionszdefiniowaną poniżej.
  2. actions- Tutaj mamy kolejną tablicę. Są to akcje, które zostaną uruchomione, gdy zostaną spełnione warunki tej reguły. Ponownie, actionnazwa każdego z nich jest tylko indeksem / kluczem tablicy tego elementu.

Teraz każdy actionma również dwa argumenty (z opcjonalnym trzecim).

  1. target- To jest element, którym chcesz manipulować w ramach tej akcji. Jeśli nie jesteś zaznajomiony ze sposobem tworzenia nazw elementów ui_component w Magento, możesz przeczytać artykuł Alana Storma . Zasadniczo jest to jak {component_name}.{component_name}.{fieldset_name}.{field_name}w tym przykładzie.
  2. callback- Oto działania, które należy podjąć w odniesieniu do wyżej wymienionych target. To wywołanie zwrotne powinno być funkcją dostępną dla elementu docelowego. W naszym przykładzie użyto hidei show. W tym miejscu możesz zacząć rozszerzać dostępne funkcje. catalog_rule_form.xmlPrzykład wspomniałem wcześniej zastosowania setValidation, jeśli chcesz zobaczyć inny przykład.
  3. Możesz także dodać <params>do każdego action, kto do nich wzywa. Możesz to również zobaczyć w catalog_rule_form.xmlprzykładzie.

Wreszcie ostatni element wewnątrz switcherConfigjest <enabled>true</enabled>. Powinno to być dość proste, bo logiczne jest włączenie / wyłączenie właśnie zaimplementowanej funkcji przełącznika.

I skończyliśmy. Więc korzystając z przykładu powyżej, co powinieneś zobaczyć to pole field2Depend1wyświetlane w przypadku wybrania opcji z wartości 2na field1i field3Depend1wyświetlona, jeśli wybierzesz opcję za pomocą wartości 3.

Ja testowałem ten przykład przy użyciu tylko hidei showna wymaganym zakresie i wydaje się, aby wziąć pod uwagę widoczność do walidacji. Innymi słowy, jeśli field2Depend1jest wymagany, będzie wymagany tylko wtedy, gdy będzie widoczny. Nie ma potrzeby dalszej konfiguracji, aby to działało.

Mam nadzieję, że zapewnia to pomoc każdemu, kto szuka bardziej gotowego rozwiązania.

rain2o
źródło
1
„Ciągi też powinny działać, ale nie przetestowałem tej teorii”. Przypadkowo przetestowałem i nie działa ... Akcje są jak tablica reguł, która musi zaczynać się od akcji 0 lub reguły 0, nie 1 lub łańcucha ...
Christophe Ferreboeuf
6

Istnieje wiele odpowiedzi na to pytanie, ale większość z nich przyjmuje albo założenia, czy uiRegistry jest w pełni załadowana, albo używa setTimeoutdo wyczyszczenia stosu wywołań i czeka na następną pętlę zdarzeń (co moim zdaniem jest nadal niewłaściwym sposobem na zrób to - ponieważ nie możesz być pewien, kiedy załadowane zostały inne elementy interfejsu - popraw mnie, jeśli się mylę).

Po pierwsze, oczywiście dodaj swój niestandardowy komponent JS do konfiguracji pola (szczegóły znajdziesz w innych odpowiedziach):

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

Oto niestandardowy składnik interfejsu użytkownika, który ukrywa lub pokazuje pola zależne - z komentarzami wyjaśniającymi, co się dzieje.

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
źródło
5

Jeśli wystąpił błąd, np. Field is UndefinedPodczas inicjalizacji widoczności pola, użyj, setTimeout()aby załadować pola zależne:

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();
},
Ronak Chauhan
źródło
Zamiast setTimeout zamiast tego użyj asynchronicznej metody uzyskiwania zależności:uiRegistry.get('q', function(field) { ... }));
Erfan
Zamiast sugerować w komentarzu i głosować w dół moją odpowiedź, możesz zamieścić tutaj swoją odpowiedź, stary, to nie jest sposób na poświęcenie odpowiedzi, sugerujesz tylko inną drogę, moja odpowiedź nie jest zła. @Erfan. Twoje głosowanie w dół robi złe wrażenie.
Ronak Chauhan
@RonakChauhan - Uzgodnione w punkcie !!! twoja odpowiedź nie jest niepoprawna, różni ludzie mają różne opinie, sugestie i rozwiązania. Twoja odpowiedź jest również poprawna !!
Manthan Dave
Oczekiwanie jednej sekundy na zainicjowanie i zablokowanie inicjalizacji jest zdecydowanie niewłaściwym sposobem. Skąd wiesz, że twoje zależności zostaną załadowane w ciągu jednej sekundy? Dlaczego nie będą to dwie sekundy? Zakładasz tutaj założenie, którego najlepiej unikać.
Erfan,
Nie ustawiłem tutaj 1 sekundy, jest to w milisekundach, SetTimeout () po prostu załaduje mój kod po załadowaniu strony, a jeśli masz odpowiedź, możesz ją opublikować. Głosuj w dół, czyjaś odpowiedź nie jest sposobem na udowodnienie, że masz rację! @Erfan
Ronak Chauhan
2

Komponent niestandardowy z 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
źródło
pokazuje „pole jest niezdefiniowane” po użyciu funkcji inicjalizacji.
Prince Patel
1
Użyj setTimeout()w, fieldDepend()ponieważ zależny nie jest jeszcze załadowany.
Ronak Chauhan
2

Istnieje kilka sposobów radzenia sobie z zależnościami pól, dla prostego menu rozwijanego Tak / Nie, pola wyboru lub przełącznika, możesz użyć właściwości importslub exportsłączenia właściwości w Magento 2. Rozwiązanie zostało szczegółowo omówione tutaj: Pola zależne w formularzach komponentów interfejsu użytkownika w Magento 2 bez Javascript dla pól boolowskich :

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

Aby obsłużyć inne typy wartości, takie jak zależność od listy wartości w menu rozwijanym lub, choć mało prawdopodobne, wartość pola wejściowego, możesz użyć switcherConfig. Aby uzyskać informacje, sprawdź pola Zależne w formularzach interfejsu użytkownika w Magento 2 bez 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>

Powyższe 2 reguły obsługują prawie wszystko za pomocą konfiguracji XML. W przypadku bardziej skomplikowanych reguł możesz także użyć JavaScript.

Każde pole w formularzu komponentu interfejsu użytkownika jest komponentem, który można rozszerzyć za pomocą componentatrybutu dla <field component="path to your js" ...>...</field>. Następnie możesz użyć tego pola, data.configaby przekazać więcej informacji do komponentu, na wypadek gdyby komponent był ogólny i został ponownie użyty w wielu miejscach, w połączeniu z właściwością importslub exportsłączącą właściwość w celu przekazania wartości do obserwowalnych lub metod.

Aby uzyskać więcej informacji na temat właściwości łączenia, możesz sprawdzić Właściwości łączenia składników interfejsu użytkownika

Hungersoft
źródło
1

Na wypadek, gdyby ktoś miał problem z rozwiązaniem Erfan , musisz podać pełną ścieżkę do pól dependentFieldNames, np .:

       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'
    ],

Nie jestem pewien, dlaczego nazwa_formy musi być 2 razy, ale to zadziałało dla mnie.

Aby to debugować, umieściłem console.log(query);w static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.js223 linii (funkcja get () tuż przed this._addRequest(query, callback))

użytkownik3722573
źródło