Magento 2.1 ¿Cómo creo que un campo de componente de formulario personalizado dependa de otro valor de campo?


13

Tengo un campo seleccionado que tiene algunas opciones. Uno de ellos tendrá algunos campos que dependen del valor, otro campo estará oculto. Copié y extendí el componente js para mi campo, pero no funcionó o lo hice de manera incorrecta. ¿El componente Ui es compatible con esta función? ¿Cómo puedo lograr esto?

A continuación se muestra lo que he hecho:

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

Respuestas:


26

Intente esto ( Nota : no olvide reemplazar la línea "Espacio de nombres" y la línea "ModuleName" con sus 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>

Dónde:

  • La visibilidad de los elementos secundarios se establece de forma predeterminada como false;
  • El visibleValue- es el field1valor cuando el elemento debe ser visible;

Espacio de nombres \ Nombre del módulo \ Modelo \ Configuración \ Fuente \ Opciones

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 seleccionado: Valor 0 seleccionado

Valor 1 seleccionado: Valor 1 seleccionado

Valor 2 seleccionado: Valor 2 seleccionado

Valor 3 seleccionado: Valor 3 seleccionado

PD: Posiblemente no sea la mejor solución, pero te ayudará


onUpdate funciona bien, pero ¿cómo hacer onLoad? ¿Cómo obtener field1.value?
zhartaunik

@zhartaunik Creo que deberías usar el initializemétodo en tu caso porque ui-element no tiene ningún onLoadmétodo. Usted puede obtener cualquier valor de campo en cualquier lugar del registro utilizando la clave de índice de entrada: uiRegistry.get('index = field1'). En caso de que tenga más preguntas, diríjase a mí en skype (sarj1989), será más fácil comunicarse en ruso.
Siarhey Uchukhlebau

Gracias @Siarhey. Decidí usar initialize. this._super, que agregar la verificación necesaria.
zhartaunik

1
No puedo obtener el valor del campo cuando estoy usando el valor del método initialize es "indefinido".
Saurabh Taletiya

1
@Siarhey Uchukhlebau ¿Puedo agregar una casilla de verificación en su lugar?
Juliano Vargas

8

La solución sugerida por Magentix arrojará un error de vez en cuando al usar initialize. Depende del tiempo que le tome a su navegador renderizar los componentes. Para solucionarlo, puede usar setTimeout.

Vea el código a continuación:

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

Está funcionando correctamente.
Dhaduk Mitesh

+1 de mi lado. Ninguno de los otros trabajos, pero este hizo mi trabajo.
Anónimo

7

Esta es una vieja pregunta con múltiples respuestas que funcionan, sin embargo, he descubierto una solución usando lo que Magento proporciona (a partir de 2.1.0) sin la necesidad de extender componentes. Como varias preguntas se han marcado como duplicadas y dirigidas aquí, pensé que sería beneficioso proporcionar alguna información sobre esta opción.

Todos los componentes de la interfaz de usuario del elemento de formulario que se extienden Magento_Ui/js/form/element/abstract.jstienen una switcherConfigconfiguración disponible para fines tales como ocultar / mostrar elementos, así como otras acciones. El switchercomponente se puede encontrar en Magento_Ui / js / form / switcher para los curiosos. Puede encontrar ejemplos de su uso en sales_rule_form.xml y catalog_rule_form.xml . Por supuesto, si ya está utilizando su propio componente personalizado, todavía puede usarlo siempre que su componente finalmente se extienda, lo abstractque parece ser el caso en función del código de ejemplo proporcionado en la pregunta.

Ahora para un ejemplo más específico para responder la pregunta original.

En Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmlsólo hay que añadir lo siguiente a los de campo settingsque realiza el control (es decir, el campo que determina qué campos están ocultos / visibles). En su ejemplo esto sería 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 a desglosarlo un poco. El switchercomponente contiene una matriz de ruleslo que estamos construyendo aquí. Cada uno <rule>tiene un nombre que es un número en este ejemplo. Este nombre es la clave / índice de matriz para este elemento. Estamos usando números como índices de matriz. Las cadenas también deberían funcionar, pero no he probado esta teoría . ACTUALIZACIÓN : como mencionó @ChristopheFerreboeuf en los comentarios, las cadenas no funcionan aquí. Estas son matrices y deberían comenzar con 0, no con cadenas o 1.

Dentro de cada uno rulepasamos dos argumentos.

  1. value- Este es el valor del field1cual debería desencadenar el actionsdefinido a continuación.
  2. actions- Aquí tenemos otra matriz. Estas son las acciones que se activarán cuando se cumplan las condiciones de esta regla. Nuevamente, actionel nombre de cada uno es solo el índice / clave de la matriz de ese elemento.

Ahora cada uno actiontiene dos argumentos también (con un tercero opcional).

  1. target- Este es el elemento que deseas manipular bajo esta acción. Si no está familiarizado con cómo se componen los nombres de elementos ui_component en Magento, puede consultar el artículo de Alan Storm . Básicamente es algo como {component_name}.{component_name}.{fieldset_name}.{field_name}en este ejemplo.
  2. callback- Aquí está la acción a tomar sobre lo mencionado anteriormente target. Esta devolución de llamada debe ser una función que esté disponible en el elemento de destino. Nuestro ejemplo usa hidey show. Aquí es donde puede comenzar a ampliar la funcionalidad disponible. El catalog_rule_form.xmlejemplo que mencioné anteriormente se usa setValidationsi desea ver un ejemplo diferente.
  3. También puede agregar <params>a cualquiera actionque lo solicite. Puedes ver esto también en el catalog_rule_form.xmlejemplo.

Finalmente el último elemento adentro switcherConfiges <enabled>true</enabled>. Esto debería ser bastante sencillo, es un booleano para habilitar / deshabilitar la funcionalidad del conmutador que acabamos de implementar.

Y ya hemos terminado. Entonces, usando el ejemplo anterior, lo que debería ver es el campo que se field2Depend1muestra si elige una opción con valor 2activado field1, y se field3Depend1muestra si elige una opción con valor 3.

He probado este ejemplo usando solo hidey showen un campo obligatorio y parece tener en cuenta la visibilidad para la validación. En otras palabras, si field2Depend1se requiere, solo se requerirá cuando sea visible. No es necesario realizar más configuraciones para que eso funcione.

Espero que esto brinde ayuda para cualquiera que busque una solución más lista para usar.


1
"Las cadenas también deberían funcionar, pero no he probado esta teoría". Probé accidentalmente y no ... Las acciones son como una matriz de reglas que debe comenzar con la acción 0 o la regla 0 no 1 o una cadena ...
Christophe Ferreboeuf

6

Hay muchas respuestas para esta pregunta, pero la mayoría de ellas hacen suposiciones sobre si el uiRegistry está completamente cargado, o lo usan setTimeoutpara borrar la pila de llamadas, y esperan el próximo bucle de eventos (que en mi opinión sigue siendo la forma incorrecta de hágalo, ya que no puede estar seguro de cuándo se han cargado los otros componentes de la interfaz de usuario, corríjame si me equivoco).

En primer lugar, por supuesto, agregue su componente JS personalizado a la configuración del campo (vea otras respuestas para más detalles):

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

Luego, aquí está el componente de interfaz de usuario personalizado que oculta o muestra los campos dependientes, con comentarios para explicar lo que está sucediendo.

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

Si tiene un error como Field is Undefinedcuando se inicializa la visibilidad del campo, use setTimeout()para cargar los campos dependientes:

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

En lugar de setTimeout, use un método asincrónico para obtener dependencias en su lugar:uiRegistry.get('q', function(field) { ... }));
Erfan

En lugar de sugerir comentarios y rechazar mi respuesta, puede publicar aquí su respuesta hermano, esta no es la forma de dedicar ninguna respuesta, solo sugiere una forma diferente, mi respuesta no es incorrecta. @Erfan. su voto negativo da una mala impresión.
Ronak Chauhan

@RonakChauhan - ¡De acuerdo sobre el punto! su respuesta no es incorrecta, diferentes personas tienen diferentes opiniones, sugerencias y soluciones. Tu respuesta también es correcta !!
Manthan Dave

Esperar un segundo para inicializar y bloquear la inicialización es claramente la forma incorrecta de hacerlo. ¿Cómo sabes que tus dependencias se cargarán en un segundo? ¿Por qué no serán dos segundos? Estás haciendo una suposición aquí, es mejor evitarlo.
Erfan

No he configurado 1 segundo aquí, está en milisegundos, SetTimeout () solo cargará mi código después de cargar la página, y si tiene su respuesta, puede publicarla. ¡Votar abajo la respuesta de alguien no es la forma de demostrar que tienes razón! @Erfan
Ronak Chauhan

2

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

se muestra "el campo no está definido" después de usar la función de inicialización.
Prince Patel

1
El uso setTimeout()de fieldDepend()porque dependía no se ha cargado todavía.
Ronak Chauhan

1

En caso de que alguien tenga problemas con la solución de Erfan , debe pasar la ruta completa a los campos dependentFieldNames, por ejemplo:

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

No estoy seguro de por qué form_name tiene que ser 2 veces, pero esto me ha funcionado.

Para depurar esto puse console.log(query);en la static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.jslínea 223 (la función get () justo antes this._addRequest(query, callback))


1

Hay algunas maneras para manejar dependencias de campo, por sencilla Sí / No desplegable, una casilla de verificación o un conmutador, puede utilizar los importso exportslas propiedades de vínculos de Magento 2. La solución se discute en detalle aquí: campos dependientes en forma de componentes de interfaz de usuario en Magento 2 sin 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 manejar otro tipo de valores, como la dependencia de una lista de valores en un menú desplegable o, aunque es poco probable, un valor de un campo de entrada, puede usar el switcherConfig. Verifique los campos dependientes en formularios de componentes ui en Magento 2 sin Javascript para obtener información.

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

Las 2 reglas anteriores, manejan casi todo usando la configuración XML. Para reglas más complejas, puede usar JavaScript también.

Cada campo en el formulario de componente de IU es un componente que puede ampliarse utilizando el componentatributo para <field component="path to your js" ...>...</field>. Luego puede usar el campo data.configpara pasar más información al componente, en caso de que el componente sea genérico y se reutilice en varios lugares, combinado con la propiedad de enlace importso exportspara pasar valores a observables o métodos.

Para obtener más información sobre las propiedades de enlace, puede consultar las propiedades de enlace de los componentes de la interfaz de usuario

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.