Magento 2.1 Wie erstelle ich ein benutzerdefiniertes Formular für Formularkomponenten, das von einem anderen Feldwert abhängt?


13

Ich habe ein Feld ausgewählt, das einige Optionen hat. In einem von ihnen hängen einige Felder vom Wert ab, in einem anderen Feld werden sie ausgeblendet. Ich habe die Komponente js für mein Feld kopiert und erweitert, aber es hat nicht funktioniert oder ich habe es falsch gemacht. UI-Komponente unterstützt diese Funktion? Wie kann ich das erreichen?

Folgendes habe ich getan:

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

Antworten:


26

Versuchen Sie dies ( Hinweis : Vergessen Sie nicht, die Zeile "Namespace" und die Zeile "ModuleName" durch Ihre Werte zu ersetzen):

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

Wo:

  • Die untergeordneten Elemente Sichtbarkeit ist standardmäßig eingestellt , wie false;
  • Das visibleValue- ist der field1Wert, wenn das Element sichtbar sein soll;

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

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

Ergebnis:

Wert 0 ausgewählt: Wert 0 ausgewählt

Wert 1 ausgewählt: Wert 1 ausgewählt

Wert 2 ausgewählt: Wert 2 ausgewählt

Wert 3 ausgewählt: Wert 3 ausgewählt

PS: Möglicherweise ist es nicht die beste Lösung, aber es soll Ihnen helfen


onUpdate funktioniert gut, aber wie macht man onLoad? Wie bekomme ich field1.value?
Zhartaunik

@zhartaunik Ich denke, Sie sollten die initializeMethode in Ihrem Fall verwenden, da ui-element keine onLoadMethode hat. Mit dem Eingabeindexschlüssel können Sie einen beliebigen Feldwert an einer beliebigen Stelle aus der Registrierung abrufen : uiRegistry.get('index = field1'). Wenn Sie weitere Fragen haben, wenden Sie sich bitte per Skype an mich (sarj1989). Die Kommunikation auf Russisch ist einfacher.
Siarhey Uchukhlebau

Danke @Siarhey. Ich habe mich für initialize entschieden. this._super, als notwendige Überprüfung hinzuzufügen.
Zhartaunik

1
Ich kann keinen Feldwert erhalten, wenn ich die Initialisierungsmethode verwende. Der Wert ist "undefiniert".
Saurabh Taletiya

1
@Siarhey Uchukhlebau Kann ich stattdessen ein Kontrollkästchen hinzufügen?
Juliano Vargas

8

Die von Magentix vorgeschlagene Lösung löst bei der Verwendung von initialize von Zeit zu Zeit einen Fehler aus. Dies hängt von der Zeit ab, die Ihr Browser zum Rendern der Komponenten benötigt. Um dies zu beheben, können Sie setTimeout verwenden.

Siehe den folgenden Code:

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

Es funktioniert richtig.
Dhaduk Mitesh

+1 von meiner Seite. Keine andere Arbeit, aber das hat meinen Job gemacht.
anonym

7

Dies ist eine alte Frage mit mehreren Antworten, die funktionieren. Ich habe jedoch eine Lösung gefunden, die Magento (ab 2.1.0) verwendet, ohne dass Komponenten erweitert werden müssen. Da mehrere Fragen als doppelt markiert und hier gerichtet wurden, hielt ich es für vorteilhaft, einige Informationen zu dieser Option bereitzustellen.

Für alle erweiterten Komponentenelement-UI-Komponenten steht Magento_Ui/js/form/element/abstract.jseine switcherConfigEinstellung zur Verfügung, die zum Ausblenden / Anzeigen von Elementen sowie für andere Aktionen verwendet werden kann. Die switcherKomponente finden Sie unter Magento_Ui / js / form / switcher für Neugierige. Beispiele dafür finden Sie in sales_rule_form.xml und catalog_rule_form.xml . Wenn Sie bereits eine eigene benutzerdefinierte Komponente verwenden, können Sie diese natürlich weiterhin verwenden, solange sich Ihre Komponente schließlich erweitert. Dies abstractscheint auf der Grundlage des in der Frage angegebenen Beispielcodes der Fall zu sein.

Nun zu einem genaueren Beispiel zur Beantwortung der ursprünglichen Frage.

In müssen Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmlSie lediglich Folgendes zu den Feldern hinzufügen settings, die das Controlling ausführen (dh das Feld, das bestimmt, welche Felder ausgeblendet / sichtbar sind). In Ihrem Beispiel wäre dies 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>

Lassen Sie es uns ein wenig aufschlüsseln. Die switcherKomponente enthält ein Array, von rulesdem wir hier bauen. Jeder <rule>hat einen Namen, der in diesem Beispiel eine Nummer ist. Dieser Name ist der Array-Schlüssel / Index für dieses Element. Wir verwenden Zahlen als Array-Indizes. Strings sollten auch funktionieren, aber ich habe diese Theorie nicht getestet . UPDATE - Wie von @ChristopheFerreboeuf in den Kommentaren erwähnt, funktionieren Zeichenfolgen hier nicht. Dies sind Arrays und sollten mit beginnen 0, nicht mit Strings oder 1.

In jedem übergeben rulewir zwei Argumente.

  1. value- Dies ist der Wert, der field1die actionsunten definierte auslösen soll .
  2. actions- Hier haben wir ein anderes Array. Dies sind die Aktionen, die ausgelöst werden, wenn die Bedingungen dieser Regel erfüllt sind. Auch hier ist jeder actionName nur der Array-Index / Schlüssel dieses Elements.

Jetzt hat jeder actionauch zwei Argumente (mit einem optionalen dritten).

  1. target- Dies ist das Element, das Sie im Rahmen dieser Aktion bearbeiten möchten. Wenn Sie nicht wissen, wie die Namen von ui_component-Elementen in Magento zusammengesetzt sind, können Sie den Artikel von Alan Storm lesen . Es ist im Grunde so etwas wie {component_name}.{component_name}.{fieldset_name}.{field_name}in diesem Beispiel.
  2. callback- Hier ist die Maßnahme zu den oben genannten target. Dieser Rückruf sollte eine Funktion sein, die für das Zielelement verfügbar ist. Unser Beispiel verwendet hideund show. Hier können Sie die verfügbaren Funktionen erweitern. Das catalog_rule_form.xmlzuvor erwähnte Beispiel wird verwendet, setValidationwenn Sie ein anderes Beispiel sehen möchten.
  3. Sie können auch <params>zu jedem hinzufügen action, der sie anfordert. Sie können dies auch im catalog_rule_form.xmlBeispiel sehen.

Schließlich ist der letzte Punkt im Inneren switcherConfigist <enabled>true</enabled>. Dies sollte ziemlich einfach sein, es ist ein Boolescher Wert zum Aktivieren / Deaktivieren der Switcher-Funktionalität, die wir gerade implementiert haben.

Und wir sind fertig. So am Beispiel über das, was sollten Sie sehen , ist Feld field2Depend1angezeigt , wenn Sie eine Option mit dem Wert wählen 2auf field1und field3Depend1angezeigt , wenn Sie eine Option mit dem Wert wählen 3.

Ich habe dieses Beispiel nur mit hideund showin einem erforderlichen Feld getestet und es scheint die Sichtbarkeit für die Validierung zu berücksichtigen. Mit anderen Worten, wenn dies field2Depend1erforderlich ist , ist es nur erforderlich, wenn es sichtbar ist. Es ist keine weitere Konfiguration erforderlich, damit dies funktioniert.

Ich hoffe, dies bietet Hilfe für alle, die nach einer sofort einsatzbereiten Lösung suchen.


1
"Strings sollten auch funktionieren, aber ich habe diese Theorie nicht getestet." Ich habe versehentlich getestet und es funktioniert nicht ... Aktionen sind als
Regelarray

6

Es gibt viele Antworten auf diese Frage, aber die meisten von ihnen gehen entweder davon aus, ob die uiRegistry vollständig geladen ist, oder verwenden sie setTimeout, um den Aufrufstapel zu löschen und auf den nächsten Eventloop zu warten (was meiner Meinung nach immer noch der falsche Weg ist Tun Sie es - da Sie nicht sicher sind, wann die anderen UI-Komponenten geladen wurden - korrigieren Sie mich, wenn ich falsch liege.

Fügen Sie zunächst natürlich Ihre benutzerdefinierte JS-Komponente zur Feldkonfiguration hinzu (Einzelheiten finden Sie in den anderen Antworten):

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

Dann ist hier die benutzerdefinierte UI-Komponente, die die abhängigen Felder verbirgt oder anzeigt - mit Kommentaren, um zu erklären, was passiert.

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

Wenn Sie Fehler Field is Undefinedbeim Initialisieren der Feldsichtbarkeit haben, verwenden Sie diese Option setTimeout(), um die abhängigen Felder zu laden:

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

Verwenden Sie anstelle von setTimeout stattdessen die asynchrone Methode zum Abrufen von Abhängigkeiten:uiRegistry.get('q', function(field) { ... }));
Erfan

Wenn Sie meine Antwort in einem Kommentar vorschlagen und abstimmen, können Sie hier Ihre Antwort posten, Bruder. Dies ist nicht der Weg, eine Antwort zu widmen. Sie schlagen nur einen anderen Weg vor. Meine Antwort ist nicht falsch. @Erfan. Ihre Ablehnung macht einen falschen Eindruck.
Ronak Chauhan

@ RonakChauhan - Einverstanden auf den Punkt !!! Ihre Antwort ist nicht falsch, verschiedene Personen haben unterschiedliche Meinungen, Vorschläge und Lösungen. Ihre Antwort ist auch richtig !!
Manthan Dave

Eine Sekunde auf die Initialisierung zu warten und die Initialisierung zu blockieren, ist ganz klar der falsche Weg. Woher wissen Sie überhaupt, dass Ihre Abhängigkeiten in einer Sekunde geladen werden? Warum sind es nicht zwei Sekunden? Sie gehen hier davon aus, dass dies am besten vermieden wird.
Erfan

Ich habe hier keine Sekunde eingestellt. In Millisekunden lädt SetTimeout () meinen Code nur nach dem Laden der Seite. Wenn Sie Ihre Antwort haben, können Sie sie veröffentlichen. Die Antwort von jemandem ist nicht der richtige Weg, um sich als richtig zu erweisen! @Erfan
Ronak Chauhan

2

Benutzerdefinierte Komponente mit 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;
        }
    });
});

Nach der Verwendung der Initialisierungsfunktion wird "Feld ist undefiniert" angezeigt.
Prinz Patel

1
Verwendung setTimeout()in, fieldDepend()da abhängig noch nicht geladen ist.
Ronak Chauhan

1

Es gibt verschiedene Möglichkeiten, um Feldabhängigkeiten zu behandeln. Für ein einfaches Dropdown-Menü Ja / Nein, ein Kontrollkästchen oder einen Umschalter können Sie die Eigenschaften importsoder exportsVerknüpfungen in Magento 2 verwenden. Die Lösung wird hier ausführlich erläutert: Abhängige Felder in Formularen für UI-Komponenten in Magento 2 ohne Javascript für boolesche Felder :

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

Um andere Arten von Werten zu behandeln, z. B. die Abhängigkeit von einer Liste von Werten in einer Dropdown-Liste oder, obwohl dies unwahrscheinlich ist, einen Wert eines Eingabefelds, können Sie die verwenden switcherConfig. Überprüfen Sie abhängige Felder in UI-Komponentenformularen in Magento 2 ohne Javascript auf Informationen.

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

Die obigen 2 Regeln behandeln so ziemlich alles mit XML-Konfiguration. Für komplexere Regeln können Sie auch JavaScript verwenden.

Jedes Feld im UI-Komponentenformular ist eine Komponente, die mithilfe des componentAttributs für erweitert werden kann <field component="path to your js" ...>...</field>. Sie können das Feld dann verwenden data.config, um weitere Informationen an die Komponente zu übergeben, falls die Komponente generisch ist und an mehreren Stellen wiederverwendet wird, kombiniert mit der Eigenschaft importsoder exportslinked, um Werte an Observables oder Methoden zu übergeben.

Weitere Informationen zu den Verknüpfungseigenschaften finden Sie unter Verknüpfungseigenschaften von UI-Komponenten


0

Nur für den Fall, dass jemand mit der Erfan- Lösung zu kämpfen hat , müssen Sie den vollständigen Pfad zu den Feldern in übergeben dependentFieldNames, z.

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

Ich bin nicht sicher, warum form_name 2 mal sein muss, aber das hat bei mir funktioniert.

Zum Debuggen dieses I put console.log(query);in static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.jsder 223. Zeile (die get () Funktion kurz vor this._addRequest(query, callback))

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.