Magento 2.1 Como eu crio o campo do componente do formulário personalizado depende de outro valor do campo?

13

Eu tenho um campo de seleção que tem algumas opções. Um deles terá alguns campos dependentes do valor, outro campo será oculto. Copiei e estendi o componente js para o meu campo, mas não funcionou ou fiz errado. O componente da interface do usuário suporta esse recurso? Como eu posso conseguir isso?

Abaixo está o que eu fiz:

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

Respostas:

26

Tente isto ( Nota : Não se esqueça de substituir a linha "Namespace" e a linha "ModuleName" pelos seus valores):

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

Onde:

  • A visibilidade dos elementos filho é definida por padrão como false;
  • O valor visibleValue- é field1quando o elemento deve estar visível;

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

Resultado:

Valor 0 selecionado: Valor 0 selecionado

Valor 1 selecionado: Valor 1 selecionado

Valor 2 selecionado: Valor 2 selecionado

Valor 3 selecionado: Valor 3 selecionado

PS: Possivelmente não é a melhor solução, mas deve ajudá-lo

Siarhey Uchukhlebau
fonte
onUpdate está funcionando bem, mas como fazer onLoad? Como obter field1.value?
Zhartaunik 11/11/16
@ zhartaunik Acho que você deve usar o initializemétodo no seu caso, porque o ui-element não tem onLoadmétodo. Você pode obter qualquer valor de campo em qualquer lugar a partir do registo utilizando a chave de índice de entrada: uiRegistry.get('index = field1'). Caso você tenha mais perguntas, entre em contato no skype (sarj1989), será mais fácil se comunicar em russo.
Siarhey Uchukhlebau 11/11
Obrigado @Siarhey. Eu decidi usar inicializar. this._super, que adicione a verificação necessária.
Zhartaunik 11/11
11
Não consigo obter o valor do campo quando estou usando o valor do método de inicialização como "indefinido".
Saurabh Taletiya 02/02
11
@Siarhey Uchukhlebau Posso adicionar a caixa de seleção?
Juliano Vargas
8

A solução sugerida pelo Magentix gera um erro de tempos em tempos ao usar o initialize. Depende do tempo que leva para o seu navegador renderizar os componentes. Para corrigi-lo, você pode usar o setTimeout.

Veja o código abaixo:

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
fonte
Está funcionando corretamente.
Dhaduk Mitesh
+1 do meu lado. Nenhum de outros trabalhos, mas isso fez o meu trabalho.
anônimo
7

Esta é uma pergunta antiga com várias respostas que funcionam, no entanto, descobri uma solução usando o que o Magento fornece (a partir do 2.1.0) sem a necessidade de estender os componentes. Como várias perguntas foram marcadas como duplicadas e direcionadas aqui, pensei que seria benéfico fornecer algumas informações sobre essa opção.

Todos os componentes da interface do usuário do elemento de formulário que se estendem Magento_Ui/js/form/element/abstract.jstêm uma switcherConfigconfiguração disponível para fins como ocultar / mostrar elementos, além de outras ações. O switchercomponente pode ser encontrado em Magento_Ui / js / form / switcher para os curiosos. Você pode encontrar exemplos de ele ser usado em sales_rule_form.xml e catalog_rule_form.xml . Obviamente, se você já estiver usando seu próprio componente personalizado, ainda poderá usá-lo, desde que o componente se estenda, o abstractque parece ser o caso, com base no código de exemplo fornecido na pergunta.

Agora, veja um exemplo mais específico para responder à pergunta original.

Em Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmlvocê simplesmente precisa adicionar o seguinte ao do campo settingsque faz o controle (ou seja, o campo que determina os campos estão escondidas / visível). No seu exemplo, isso seria 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>

Vamos dividir um pouco. O switchercomponente contém uma matriz do rulesque estamos construindo aqui. Cada <rule>um tem um nome que é um número neste exemplo. Este nome é a chave / índice da matriz para este item. Estamos usando números como índices de matriz. Strings devem funcionar também, mas eu não testei essa teoria . ATUALIZAÇÃO - Conforme mencionado por @ChristopheFerreboeuf nos comentários, as strings não funcionam aqui. Essas são matrizes e devem começar com 0, não cadeias ou 1.

Dentro de cada um rule, passamos dois argumentos.

  1. value- Este é o valor do field1qual deve disparar o actionsdefinido abaixo.
  2. actions- Aqui temos outra matriz. Estas são as ações a serem acionadas quando as condições desta regra forem atendidas. Novamente, actiono nome de cada um é apenas o índice / chave da matriz desse item.

Agora, cada um actiontem dois argumentos também (com um terceiro opcional).

  1. target- Este é o elemento que você deseja manipular sob esta ação. Se você não está familiarizado com a forma como os nomes dos elementos ui_component são compostos no Magento, consulte o artigo de Alan Storm . É basicamente algo como {component_name}.{component_name}.{fieldset_name}.{field_name}neste exemplo.
  2. callback- Aqui está a ação a ser tomada sobre o mencionado acima target. Esse retorno de chamada deve ser uma função disponível no elemento de destino. Nosso exemplo usa hidee show. É aqui que você pode começar a expandir a funcionalidade disponível. O catalog_rule_form.xmlexemplo que mencionei anteriormente usa setValidationse você deseja ver um exemplo diferente.
  3. Você também pode adicionar <params>a qualquer um actionque pedir por eles. Você pode ver isso no catalog_rule_form.xmlexemplo também.

Finalmente, o último item dentro switcherConfigé <enabled>true</enabled>. Isso deve ser bem simples: é um booleano ativar / desativar a funcionalidade do comutador que acabamos de implementar.

E nós terminamos. Então, usando o exemplo acima que você deve ver é o campo field2Depend1exibido se você escolher uma opção com valor 2em field1e field3Depend1exibida se você escolher uma opção com valor 3.

Testei este exemplo usando apenas hidee showem um campo obrigatório e parece levar em consideração a visibilidade para validação. Em outras palavras, se field2Depend1for necessário, será necessário apenas quando visível. Não há necessidade de configuração adicional para que isso funcione.

Espero que isso ofereça alguma ajuda para quem procura uma solução mais pronta para uso.

rain2o
fonte
11
"Strings devem funcionar também, mas eu não testei essa teoria." Eu acidentalmente testado e isso não acontece ... As ações são como regras matriz que deve começar com a ação 0 ou excluir 0 não 1 ou uma corda ...
Christophe Ferreboeuf
6

Há muitas respostas para essa pergunta, mas a maioria delas faz suposições sobre se o uiRegistry está totalmente carregado ou usa setTimeout para limpar a pilha de chamadas e aguarda o próximo loop de evento (que, na minha opinião, ainda é o caminho errado) faça - já que você não pode ter certeza de quando os outros componentes da interface do usuário foram carregados - me corrija se eu estiver errado).

Primeiramente, é claro, adicione seu componente JS personalizado à configuração do campo (consulte outras respostas para obter detalhes):

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

Então, aqui está o componente de interface do usuário personalizado que oculta ou mostra os campos dependentes - com comentários para explicar o que está acontecendo.

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

Se você tiver um erro como Field is Undefinedquando a visibilidade do campo foi inicializada, use setTimeout()para carregar os campos dependentes:

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
fonte
Em vez de setTimeout, use o método assíncrono para obter dependências:uiRegistry.get('q', function(field) { ... }));
Erfan
Em vez de sugerir no comentário e votar em baixa a minha resposta, você pode postar aqui a sua resposta mano, Esta não é a maneira de dedicar qualquer resposta, Você está apenas sugerindo uma maneira diferente, minha resposta não está errada. @Erfan. seu voto negativo causa uma impressão errada.
Ronak Chauhan
@RonakChauhan - Concordou em questão !!! sua resposta não está incorreta, pessoas diferentes têm opiniões, sugestões e soluções diferentes. Sua resposta também está correta !!
Manthan Dave
Esperar um segundo para inicializar e bloquear a inicialização é claramente a maneira errada de fazê-lo. Como você sabe que suas dependências serão carregadas em um segundo? Por que não vai demorar dois segundos? Você está fazendo uma suposição aqui, é melhor evitar.
precisa saber é o seguinte
Eu não configurei 1 segundo aqui, é em milissegundos, SetTimeout () apenas carrega meu código depois de carregar a página, e se você tiver sua resposta, poderá publicá-la. Resposta Down-voto de alguém não é a maneira de provar a si mesmo direito @Erfan!
Ronak Chauhan
2

Componente personalizado com 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
fonte
é show "campo indefinido" depois de usar a função de inicialização.
Prince Patel
11
Use setTimeout()em fieldDepend()causa dependia não é carregado ainda.
Ronak Chauhan
1

Apenas no caso de alguém ter problemas com a solução Erfan , é necessário passar o caminho completo para os campos dependentFieldNames, por exemplo:

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

Não sei por que o form_name deve ser 2 vezes, mas isso funcionou para mim.

Para depurar isso, coloquei console.log(query);na static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.js223ª linha (a função get () logo antes this._addRequest(query, callback))

user3722573
fonte
1

Existem algumas maneiras de lidar com dependências de campo, para uma lista suspensa Sim / Não simples, uma caixa de seleção ou um comutador, você pode usar as propriedades de vinculação importsou exportsno Magento 2. A solução é discutida em detalhes aqui: Campos dependentes nos formulários de componentes de interface do usuário no Magento 2 sem Javascript para campos booleanos :

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

Para manipular outros tipos de valores, como dependência de uma lista de valores em uma lista suspensa ou, embora improvável, o valor de um campo de entrada, você pode usar o switcherConfig. Verifique os campos dependentes nos formulários ui-component no Magento 2 sem Javascript para obter informações.

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

As 2 regras acima, lidam com praticamente tudo usando a configuração XML. Para regras mais complexas, você também pode usar JavaScript.

Cada campo no formulário de componente da interface do usuário é um componente que pode ser estendido usando o componentatributo para o <field component="path to your js" ...>...</field>. Em seguida, você pode usar o campo data.configpara passar mais informações ao componente, caso o componente seja genérico e reutilizado em vários locais, combinado com o importsouexports propriedade link para transmitir valores a métodos ou objetos observáveis.

Para obter mais informações sobre as propriedades de vinculação, verifique as propriedades de vinculação dos componentes da interface do usuário.

hungersoft
fonte