Magento 2.1 Bagaimana cara membuat kustom bidang komponen formulir tergantung pada nilai bidang lain?

13

Saya memiliki satu bidang pilih yang memiliki beberapa opsi. Salah satunya akan memiliki beberapa bidang bergantung pada nilai, bidang lain akan disembunyikan. Saya telah menyalin dan memperluas js komponen untuk bidang saya tetapi tidak berhasil atau saya melakukannya dengan cara yang salah. Komponen Ui mendukung fitur ini? Bagaimana saya bisa mencapai ini?

Di bawah ini yang saya lakukan:

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

Jawaban:

26

Coba ini ( Catatan : Jangan lupa mengganti baris "Namespace" dan baris "ModuleName" dengan nilai Anda):

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

Dimana:

  • Visibilitas elemen anak diatur secara default sebagai false;
  • Nilai visibleValue- adalah field1ketika elemen harus terlihat;

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

Hasil:

Nilai 0 dipilih: Nilai 0 dipilih

Nilai 1 dipilih: Nilai 1 dipilih

Nilai 2 dipilih: Nilai 2 dipilih

Nilai 3 dipilih: Nilai 3 dipilih

PS: Mungkin itu bukan solusi yang terbaik, tetapi itu akan membantu Saudara

Siarhey Uchukhlebau
sumber
onUpdate berfungsi dengan baik, tetapi bagaimana membuat onLoad? Bagaimana cara mendapatkan field1.value?
zhartaunik
@ Zhartaunik Saya pikir Anda harus menggunakan initializemetode ini dalam kasus Anda karena ui-elemen tidak memiliki onLoadmetode. Anda bisa mendapatkan nilai lapangan di setiap tempat dari registry menggunakan kunci indeks masukan: uiRegistry.get('index = field1'). Jika Anda memiliki pertanyaan lebih lanjut, silakan, hubungi saya di skype (sarj1989) akan lebih mudah untuk berkomunikasi dalam bahasa Rusia.
Siarhey Uchukhlebau
Terima kasih @ Siewhey. Saya memutuskan untuk menggunakan inisialisasi. this._super, daripada menambahkan verifikasi yang diperlukan.
zhartaunik
1
Saya tidak bisa mendapatkan nilai bidang ketika saya menggunakan nilai metode inisialisasi adalah "tidak terdefinisi".
Saurabh Taletiya
1
@Siarhey Uchukhlebau Bisakah saya menambahkan kotak centang?
Juliano Vargas
8

Solusi yang disarankan oleh Magentix akan melemparkan kesalahan dari waktu ke waktu saat menggunakan inisialisasi. Tergantung pada waktu yang dibutuhkan browser Anda untuk membuat komponen. Untuk memperbaikinya Anda bisa menggunakan setTimeout.

Lihat kode di bawah ini:

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
sumber
Ini berfungsi dengan baik.
Dhaduk Mitesh
+1 dari sisi saya. Tidak ada pekerjaan lain selain ini yang melakukan pekerjaan saya.
Anonim
7

Ini adalah pertanyaan lama dengan beberapa jawaban yang berfungsi, namun saya telah menemukan solusi menggunakan apa yang Magento sediakan (pada 2.1.0) tanpa perlu memperluas komponen. Karena beberapa pertanyaan telah ditandai sebagai rangkap dan diarahkan di sini saya pikir akan bermanfaat untuk memberikan beberapa informasi tentang opsi ini.

Semua komponen elemen bentuk ui yang memperluas Magento_Ui/js/form/element/abstract.jsmemiliki switcherConfigpengaturan yang tersedia untuk tujuan seperti menyembunyikan / menunjukkan elemen serta tindakan lainnya. The switcherkomponen dapat ditemukan di Magento_Ui / js / form / switcher untuk penasaran. Anda dapat menemukan contoh penggunaannya di sales_rule_form.xml dan catalog_rule_form.xml . Tentu saja jika Anda sudah menggunakan komponen kustom Anda sendiri, Anda masih dapat menggunakan ini selama komponen Anda pada akhirnya meluas abstractyang tampaknya merupakan kasus berdasarkan pada contoh kode yang disediakan dalam pertanyaan.

Sekarang untuk contoh yang lebih spesifik untuk menjawab pertanyaan awal.

Dalam Namespace/ModuleName/view/adminhtml/ui_component/your_entity_form.xmlAnda hanya perlu menambahkan berikut ke bidang settingsyang melakukan pengontrolan (yaitu bidang yang menentukan bidang mana yang tersembunyi / terlihat). Dalam contoh Anda ini akan menjadi 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>

Mari kita jabarkan sedikit. The switcherkomponen berisi array rulesyang adalah apa yang kita membangun di sini. Masing-masing <rule>memiliki nama yang merupakan angka dalam contoh ini. Nama ini adalah kunci larik / indeks untuk item ini. Kami menggunakan angka sebagai indeks array. String harus bekerja juga, tetapi saya belum menguji teori ini . PEMBARUAN - Seperti yang disebutkan oleh @ChristopheFerreboeuf dalam komentar, string untuk tidak berfungsi di sini. Ini adalah array dan harus dimulai dengan 0, bukan string atau 1.

Di dalam masing-masing rulekita melewati dua argumen.

  1. value- Ini adalah nilai field1yang harus memicu yang actionsditentukan di bawah ini.
  2. actions- Di sini kita memiliki array lain. Ini adalah tindakan yang akan dipicu ketika kondisi aturan ini dipenuhi. Sekali lagi, actionnama masing - masing hanyalah indeks array / kunci dari item itu.

Sekarang masing-masing actionmemiliki dua argumen juga (dengan ke-3 opsional).

  1. target- Ini adalah elemen yang ingin Anda manipulasi di bawah tindakan ini. Jika Anda tidak terbiasa dengan bagaimana nama elemen ui_component disusun di Magento, Anda dapat memeriksa artikel Alan Storm . Ini pada dasarnya seperti {component_name}.{component_name}.{fieldset_name}.{field_name}dalam contoh ini.
  2. callback- Berikut adalah tindakan yang harus diambil pada yang disebutkan di atas target. Panggilan balik ini harus berupa fungsi yang tersedia pada elemen yang ditargetkan. Contoh kami menggunakan hidedan show. Di sinilah Anda dapat mulai memperluas fungsionalitas yang tersedia. The catalog_rule_form.xmlcontoh yang saya sebutkan penggunaan sebelumnya setValidationjika Anda ingin melihat contoh yang berbeda.
  3. Anda juga dapat menambahkan <params>apa pun actionyang memanggil mereka. Anda dapat melihat ini dalam catalog_rule_form.xmlcontoh juga.

Akhirnya item terakhir di dalamnya switcherConfigadalah <enabled>true</enabled>. Ini seharusnya cukup mudah, ini adalah Boolean untuk mengaktifkan / menonaktifkan fungsi switcher yang baru saja kami implementasikan.

Dan kita sudah selesai. Jadi menggunakan contoh di atas apa yang harus Anda lihat adalah bidang yang field2Depend1ditampilkan jika Anda memilih opsi dengan nilai 2aktif field1, dan field3Depend1ditampilkan jika Anda memilih opsi dengan nilai 3.

Saya telah menguji contoh ini hanya dengan menggunakan hidedan showpada bidang yang diperlukan dan itu tampaknya mempertimbangkan visibilitas untuk validasi. Dengan kata lain, jika field2Depend1diperlukan hanya akan diperlukan saat terlihat. Tidak perlu konfigurasi lebih lanjut untuk itu berfungsi.

Semoga ini memberikan bantuan bagi siapa pun yang mencari solusi yang lebih out-of-the-box.

rain2o
sumber
1
"Senar seharusnya bekerja juga, tapi aku belum menguji teori ini." Saya tidak sengaja menguji dan tidak ... Tindakan adalah sebagai aturan aturan yang harus dimulai dengan tindakan 0 atau aturan 0 bukan 1 atau string ...
Christophe Ferreboeuf
6

Ada banyak jawaban untuk pertanyaan ini, tetapi kebanyakan dari mereka membuat asumsi apakah uiRegistry sudah terisi penuh, atau digunakan setTimeoutuntuk menghapus tumpukan panggilan, dan menunggu eventloop berikutnya (yang menurut saya masih merupakan cara yang salah untuk lakukan itu - karena Anda tidak dapat memastikan kapan komponen ui lainnya telah dimuat - koreksi saya jika saya salah).

Pertama, tentu saja, tambahkan komponen JS khusus Anda ke konfigurasi bidang (lihat jawaban lain untuk detail):

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

Lalu, inilah komponen UI khusus yang menyembunyikan atau menampilkan bidang dependen - dengan komentar untuk menjelaskan apa yang terjadi.

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

Jika Anda mendapat kesalahan seperti Field is Undefinedsaat visibilitas bidang yang diinisialisasi, gunakan setTimeout()untuk memuat bidang yang tergantung:

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
sumber
Alih-alih setTimeout, gunakan metode asinkron untuk mendapatkan dependensi sebagai gantinya:uiRegistry.get('q', function(field) { ... }));
Erfan
Instan menyarankan dalam komentar dan down-vote jawaban saya Anda dapat Posting di sini jawaban Anda bro, Ini bukan cara untuk mencurahkan jawaban apa pun, Anda hanya menyarankan cara yang berbeda, jawaban saya tidak salah. @Erfan. down-vote Anda membuat kesan yang salah.
Ronak Chauhan
@RonakChauhan - Setuju pada poin !!! jawaban Anda tidak salah, orang yang berbeda memiliki pendapat, saran dan solusi yang berbeda. Jawaban Anda juga benar !!
Manthan Dave
Menunggu satu detik untuk diinisialisasi, dan memblokir inisialisasi jelas merupakan cara yang salah untuk melakukannya. Bagaimana Anda tahu dependensi Anda akan dimuat dalam satu detik? Mengapa tidak dua detik? Anda membuat asumsi di sini, sebaiknya dihindari.
Erfan
Saya belum menetapkan 1 detik di sini, Ini dalam milidetik, SetTimeout () hanya akan memuat kode saya setelah memuat halaman, Dan jika Anda memiliki jawaban Anda maka Anda dapat mempostingnya. Turunkan suara jawaban seseorang bukanlah cara untuk membuktikan diri Anda benar! @ Erfan
Ronak Chauhan
2

Komponen khusus dengan 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
sumber
itu menunjukkan "bidang tidak terdefinisi" setelah menggunakan fungsi inisialisasi.
Pangeran Patel
1
Gunakan setTimeout()di fieldDepend()karena tergantung tidak dimuat belum.
Ronak Chauhan
1

Untuk berjaga-jaga seandainya seseorang kesulitan dengan solusi Erfan , Anda harus melewati jalur penuh ke bidang dependentFieldNames, misalnya:

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

Saya tidak yakin mengapa form_name harus 2 kali, tetapi ini berhasil bagi saya.

Untuk debug ini saya taruh console.log(query);di static/adminhtml/Magento/backend/en_US/Magento_Ui/js/lib/registry/registry.jsbaris ke 223 (fungsi get () sebelum this._addRequest(query, callback))

pengguna3722573
sumber
1

Ada beberapa cara untuk menangani dependensi bidang, untuk dropdown Ya / Tidak sederhana, kotak centang atau switcher, Anda dapat menggunakan importsatau exportsmenghubungkan properti di Magento 2. Solusi dibahas secara rinci di sini: Bidang dependen dalam bentuk komponen UI di Magento 2 tanpa Javascript untuk bidang boolean :

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

Untuk menangani tipe nilai lainnya seperti ketergantungan pada daftar nilai dalam dropdown atau meskipun tidak mungkin, nilai bidang input, Anda bisa menggunakan switcherConfig. Periksa bidang Dependen dalam bentuk komponen ui di Magento 2 tanpa Javascript untuk informasi.

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

2 aturan di atas, menangani hampir semua menggunakan konfigurasi XML. Untuk aturan yang lebih kompleks, Anda dapat menggunakan JavaScript juga.

Setiap bidang dalam bentuk komponen UI adalah komponen yang dapat diperluas menggunakan componentatribut untuk <field component="path to your js" ...>...</field>. Anda kemudian dapat menggunakan bidang ini data.configuntuk meneruskan lebih banyak informasi ke komponen, jika komponen itu generik dan digunakan kembali di beberapa tempat, dikombinasikan dengan importsatau exportsmenghubungkan properti untuk meneruskan nilai ke observasi atau metode.

Untuk informasi lebih lanjut tentang properti penautan Anda dapat memeriksa Properti penautan komponen UI

Hungersoft
sumber