Magento 2.1 Come faccio a creare campi personalizzati per i componenti del modulo dipende da un altro valore di campo?


13

Ho un campo selezionare che ha alcune opzioni. Uno di questi avrà alcuni campi dipendenti dal valore, un altro campo sarà nascosto. Ho copiato ed esteso i componenti js per il mio campo ma non ha funzionato o l'ho fatto nel modo sbagliato. Il componente dell'interfaccia utente supporta questa funzione? Come posso raggiungere questo obiettivo?

Di seguito è quello che ho fatto:

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

Risposte:


26

Prova questo ( Nota : non dimenticare di sostituire la riga "Namespace" e la riga "ModuleName" con i tuoi valori):

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

Dove:

  • La visibilità degli elementi figlio è impostata di default come false;
  • Il visibleValue- è field1valore quando l'elemento dovrebbe essere visibile;

Spazio dei nomi \ ModuleName \ modello \ 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 / elemento / 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();
        },
    });
});

Risultato:

Valore 0 selezionato: Valore 0 selezionato

Valore 1 selezionato: Valore 1 selezionato

Valore 2 selezionato: Valore 2 selezionato

Valore 3 selezionato: Valore 3 selezionato

PS: Forse non è la soluzione migliore, ma ti aiuterà


onUpdate funziona bene, ma come fare onLoad? Come ottenere field1.value?
Zhartaunik,

@zhartaunik Penso che dovresti usare il initializemetodo nel tuo caso perché ui-element non ha onLoadmetodo. È possibile ottenere qualsiasi valore di campo in qualsiasi luogo dal Registro di sistema utilizzando la chiave di indice di ingresso: uiRegistry.get('index = field1'). Nel caso abbiate ulteriori domande, per favore, contattatemi su skype (sarj1989), sarà più facile comunicare in russo.
Siarhey Uchukhlebau,

Grazie @Siarhey. Ho deciso di utilizzare l'inizializzazione. this._super, che aggiungere la verifica necessaria.
Zhartaunik,

1
Non riesco a ottenere il valore del campo quando sto utilizzando il valore del metodo di inizializzazione "non definito".
Saurabh Taletiya,

1
@Siarhey Uchukhlebau Posso invece aggiungere la casella?
Juliano Vargas,

8

La soluzione suggerita da Magentix genererà di volta in volta un errore quando si utilizza l'inizializzazione. Dipende dal tempo impiegato dal browser per eseguire il rendering dei componenti. Per risolverlo puoi usare setTimeout.

Vedi il codice qui sotto:

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

Funziona correttamente.
Dhaduk Mitesh,

+1 dalla mia parte. Nessuno degli altri lavori, ma questo ha fatto il mio lavoro.
anonimo

7

Questa è una vecchia domanda con risposte multiple che funzionano, tuttavia ho scoperto una soluzione usando ciò che Magento fornisce (a partire dalla 2.1.0) senza la necessità di estendere i componenti. Poiché più domande sono state contrassegnate come duplicate e dirette qui, ho pensato che sarebbe stato utile fornire alcune informazioni su questa opzione.

Tutti i componenti dell'interfaccia utente del modulo che si estendono Magento_Ui/js/form/element/abstract.jshanno switcherConfigun'impostazione disponibile per scopi come nascondere / mostrare elementi e altre azioni. Il switchercomponente può essere trovato su Magento_Ui / js / form / switcher per i curiosi. Puoi trovarne degli esempi usati in sales_rule_form.xml e catalog_rule_form.xml . Naturalmente, se stai già utilizzando il tuo componente personalizzato, puoi comunque utilizzarlo fino a quando il componente alla fine si estende, il abstractche sembra essere il caso in base al codice di esempio fornito nella domanda.

Ora per un esempio più specifico di rispondere alla domanda originale.

In Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmldevi semplicemente aggiungere quanto segue al campo settingsche fa il controllo (cioè il campo che determina quali campi sono nascosti / visibili). Nel tuo esempio questo sarebbe 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>

Analizziamolo un po '. Il switchercomponente contiene una matrice di rulescui è ciò che stiamo costruendo qui. Ognuno <rule>ha un nome che è un numero in questo esempio. Questo nome è la chiave / indice dell'array per questo elemento. Stiamo usando i numeri come indici di array. Anche le stringhe dovrebbero funzionare ma non ho testato questa teoria . AGGIORNAMENTO - Come menzionato da @ChristopheFerreboeuf nei commenti, le stringhe non funzionano qui. Questi sono array e dovrebbero iniziare con 0, non stringhe o 1.

All'interno di ciascuno rulepassiamo due argomenti.

  1. value- Questo è il valore di field1cui dovrebbe attivare il actionsdefinito di seguito.
  2. actions- Qui abbiamo un altro array. Queste sono le azioni da attivare quando vengono soddisfatte le condizioni di questa regola. Ancora una volta, actionil nome di ciascuno è solo l'indice / chiave dell'array di quell'elemento.

Ora ognuno actionha anche due argomenti (con un terzo opzionale).

  1. target- Questo è l'elemento che desideri manipolare nell'ambito di questa azione. Se non hai familiarità con il modo in cui i nomi degli elementi ui_component sono composti in Magento puoi consultare l'articolo di Alan Storm . È praticamente qualcosa di simile {component_name}.{component_name}.{fieldset_name}.{field_name}a questo esempio.
  2. callback- Ecco le azioni da intraprendere su quanto sopra target. Questo callback dovrebbe essere una funzione disponibile sull'elemento target. Il nostro esempio usa hidee show. È qui che puoi iniziare ad espandere le funzionalità disponibili. L' catalog_rule_form.xmlesempio che ho citato in precedenza utilizza setValidationse si desidera vedere un esempio diverso.
  3. Puoi anche aggiungere <params>quelli actionche li richiedono. Puoi vederlo anche catalog_rule_form.xmlnell'esempio.

Finalmente l'ultimo oggetto switcherConfigè <enabled>true</enabled>. Questo dovrebbe essere piuttosto semplice, è un booleano abilitare / disabilitare la funzionalità switcher che abbiamo appena implementato.

E abbiamo finito. Quindi usando l'esempio sopra quello che dovresti vedere è il campo field2Depend1visualizzato se scegli un'opzione con valore 2attivo field1, e field3Depend1visualizzato se scegli un'opzione con valore 3.

Ho provato questo esempio usando solo hidee showsu un campo obbligatorio e sembra tenere conto della visibilità per la convalida. In altre parole, se field2Depend1richiesto, sarà richiesto solo quando visibile. Non è necessaria un'ulteriore configurazione affinché funzioni.

Spero che questo offra un po 'di aiuto a chiunque cerchi una soluzione pronta all'uso.


1
"Anche le stringhe dovrebbero funzionare ma non ho testato questa teoria." Ho accidentalmente testato e non ... Le azioni sono come array di regole che deve iniziare con l'azione 0 o la regola 0 non 1 o una stringa ...
Christophe Ferreboeuf

6

Ci sono molte risposte a questa domanda, ma la maggior parte di loro fa delle ipotesi sul fatto che uiRegistry sia completamente caricato, oppure usa setTimeoutper cancellare lo stack di chiamate e aspetta il prossimo eventloop (che secondo me è ancora il modo sbagliato di fallo - dato che non puoi essere sicuro quando gli altri componenti dell'interfaccia utente sono stati caricati - correggimi se sbaglio).

Innanzitutto, ovviamente, aggiungi il tuo componente JS personalizzato alla configurazione del campo (vedi altre risposte per i dettagli):

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

Quindi, ecco il componente dell'interfaccia utente personalizzata che nasconde o mostra i campi dipendenti, con commenti per spiegare cosa sta succedendo.

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

Se si verifica un errore come Field is Undefinedquando si inizializza la visibilità dei campi, utilizzare setTimeout()per caricare i campi dipendenti:

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

Invece di setTimeout, usa invece il metodo asincrono per ottenere dipendenze:uiRegistry.get('q', function(field) { ... }));
Erfan

Istituito a suggerire un commento e un voto negativo alla mia risposta, puoi pubblicare qui la tua risposta, fratello, questo non è il modo di dedicare una risposta, stai solo suggerendo un modo diverso, la mia risposta non è sbagliata. @Erfan. il tuo voto negativo fa un'impressione sbagliata.
Ronak Chauhan,

@RonakChauhan - Concordato sul punto !!! la tua risposta non è errata, persone diverse hanno opinioni, suggerimenti e soluzioni diversi. Anche la tua risposta è corretta !!
Manthan Dave,

Attendere un secondo per inizializzare e bloccare l'inizializzazione è chiaramente il modo sbagliato di farlo. Come fai a sapere se le tue dipendenze verranno caricate in un secondo? Perché non saranno due secondi? Stai assumendo un presupposto qui, è meglio evitarlo.
Erfan,

Non ho impostato 1 secondo qui, è in millisecondi, SetTimeout () caricherà il mio codice dopo aver caricato la pagina, e se hai la tua risposta puoi pubblicarla. Sottovalutare la risposta di qualcuno non è il modo di mettersi alla prova! @Erfan
Chauhan

2

Componente personalizzato con 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;
        }
    });
});

mostra "il campo non è definito" dopo aver usato la funzione di inizializzazione.
Prince Patel,

1
Utilizzare setTimeout()in fieldDepend()perché dipendente non è ancora stato caricato.
Ronak Chauhan,

1

Nel caso in cui qualcuno abbia difficoltà con la soluzione Erfan , devi passare l'intero percorso ai campi dependentFieldNames, ad esempio:

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

Non sono sicuro del perché form_name debba essere 2 volte, ma questo ha funzionato per me.

Per eseguire il debug questo ho messo console.log(query);nel static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.js223o linea (la get () funzione poco prima this._addRequest(query, callback))


1

Esistono alcuni modi per gestire le dipendenze dei campi, per un semplice menu a discesa Sì / No, una casella di controllo o un commutatore, è possibile utilizzare le proprietà importso il exportscollegamento in Magento 2. La soluzione è discussa in dettaglio qui: Campi dipendenti nei moduli componenti dell'interfaccia utente in Magento 2 senza Javascript per i campi booleani :

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

Per gestire altri tipi di valori come la dipendenza da un elenco di valori in un menu a discesa o, sebbene improbabile, un valore di un campo di input, è possibile utilizzare il switcherConfig. Controlla i campi dipendenti nei moduli ui-componente in Magento 2 senza Javascript per informazioni.

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

Le 2 regole precedenti, gestiscono praticamente tutto usando la configurazione XML. Per regole più complesse, puoi usare anche JavaScript.

Ogni campo nel modulo del componente dell'interfaccia utente è un componente che può essere esteso utilizzando l' componentattributo per il <field component="path to your js" ...>...</field>. È quindi possibile utilizzare il campo data.configper passare più informazioni al componente, nel caso in cui il componente sia generico e venga riutilizzato in più posizioni, combinato con la proprietà importso exportslinking per passare valori a osservabili o metodi.

Per ulteriori informazioni sulle proprietà di collegamento è possibile selezionare Proprietà di collegamento dei componenti dell'interfaccia utente

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.