Tabs sering digunakan dalam pembuatan menu horizontal maupun vertical. Tidak menutup kemungkinan, aplikasi web yang Anda bangun nantinya juga akan memanfaatkan Tabs untuk keperluan navigasi. Di materi sebelumnya, saya telah membahas Pembuatan Komponen di Dalam Komponen Vue.js dan kali ini saya akan menunjukkan teknik lanjutan dalam penerapan child component untuk tabs.

Membuat Konten Tab

Sekarang buat file latihan011.html, lalu copy paste baris kode berikut :

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Latihan Vue 011</title>
    <link rel="stylesheet" href="bootstrap.min.css">
</head>
<body>
    <div id="app">
        <div class="jumbotron text-center">
            <h1>Bootstrap 4</h1>
            <p>Latihan011 - Contoh Komponen Tabs Pada Vue.js</p>
          </div>
        <div class="container">
            <div class="row">
                <div class="col-md-12">

                    <tabs>

                        <tab klub="Juventus">
                            <p>Dybala, Higuain, Ronaldo</p>
                        </tab>

                        <tab klub="Real Madrid">
                            <p>Bale, Benzema, Hazard</p>
                        </tab>
                        
                        <tab klub="Liverpool">
                            <p>Firmino, Mane, Salah</p>
                        </tab>

                    </tabs>

                </div>
            </div>
        </div>
    </div>
    <script src="vue.js"></script>
    <script src="latihan011.js"></script>
</body>
</html>

dan juga file latihan011.js dengan baris kode :

Vue.component('tabs', {
    template: `
        <div>
            
            <ul class="nav nav-tabs"></ul>

            <div class="tab-content">
                <slot></slot>
            </div>

        </div>
    `
});

new Vue ({    
    el: '#app'
});

Pada kode di atas, saya membuat satu komponen bernama tabs (di dalamnya akan terdapat beberapa tab). Option template terdiri dari elemen <ul class="nav nav-tabs"></ul> yang digunakan untuk menampilkan link navigasi berpindah antar tab dan elemen <div class="tab-content"><slot></slot></div> untuk memuat isi / konten dari setiap tab. Sebagai permulaan, <slot></slot> saya isi dengan :

<tab klub="Juventus">
    <p>Dybala, Higuain, Ronaldo</p>
</tab>

<tab klub="Real Madrid">
    <p>Bale, Benzema, Hazard</p>
</tab>

<tab klub="Liverpool">
    <p>Firmino, Mane, Salah</p>
</tab>

Eksekusi awal file latihan011.html diperoleh hasil :

contoh komponen tabs vue js

Selanjutnya, daftarkan child component tab untuk parent component tabs, sisipkan baris kode berikut pada latihan011.js :

Vue.component('tab', {
    template: `
        <div>
            <slot></slot>
        </div>
    `
});

tepat di atas :

new Vue ({    
    el: '#app'
});

Setelah child komponen tab dibuat, eksekusi file latihan011.html, hasilnya seperti di bawah :

contoh komponen tabs vue js

Berbeda dari sebelumnya, elemen <tab klub="Juventus"></tab>, <tab klub="Real Madrid"></tab> dan <tab klub="Liverpool"></tab> di halaman HTML kini sudah menerapkan template yang ada di dalam Vue.component('tab',{}). Melalui Inspect Element browser, dapat dilihat perbedaan hasil rendering sebelum komponen tab didaftarkan (tanpa template) :

<tab klub="Juventus">
    <p>Dybala, Higuain, Ronaldo</p>
</tab>

<tab klub="Real Madrid">
    <p>Bale, Benzema, Hazard</p>
</tab>

<tab klub="Liverpool">
    <p>Firmino, Mane, Salah</p>
</tab>

dengan setelah komponen tab didaftarkan (memakai template) :

<div klub="Juventus">
    <p>Dybala, Higuain, Ronaldo</p>
</div>

<div klub="Real Madrid">
    <p>Bale, Benzema, Hazard</p>
</div>

<div klub="Liverpool">
    <p>Firmino, Mane, Salah</p>
</div>

Membuat Link Navigasi Tab

Jika sebelumnya <tab klub="Juventus">, <tab klub="Real Madrid"> dan <tab klub="Liverpool"> di-render untuk isi / konten tab melalui <div class="tab-content"><slot></slot></div>, sekarang ketiganya akan kita gunakan kembali untuk me-render link navigasi berpindah antar tab di elemen <ul class="nav nav-tabs"></ul> (lihat option template pada komponen tabs).

Perbarui file latihan011.js dengan copy paste baris kode berikut :

Vue.component('tabs', {
    template: `
        <div>
            
            <ul class="nav nav-tabs">
                <li v-for="tab in klubSepakBola" class="nav-item">
                    <a class="nav-link" href="Javascript:void(0)">{{ tab.klub }}</a>
                </li>
            </ul>

            <div class="tab-content">
                <slot></slot>
            </div>

        </div>
    `,
    data() {
        return { 
            klubSepakBola: [] 
        }
    },
    created() {
        this.klubSepakBola = this.$children;
    }
});

Vue.component('tab', {
    props: ['klub'],
    template: `
        <div>
            <slot></slot>
        </div>
    `
});

new Vue ({    
    el: '#app'
});

Urutan cara membaca kode di atas :

  1. Kita sudah terlebih dahulu mengisi <slot></slot> di <div class="tab-content"><slot></slot></div> dengan <tab klub="Juventus">, <tab klub="Real Madrid"> dan <tab klub="Liverpool">.
  2. Ketika menuliskan elemen <tab klub="Juventus">, <tab klub="Real Madrid"> dan <tab klub="Liverpool">, berarti kita memanggil komponen tab dari Vue.component('tab',{}) dan me-render template untuk ketiga elemen tersebut.
  3. Pemanggilan komponen Vue.component('tab',{}) pada poin kedua menyertakan klub="Juventus", klub="Real Madrid" dan klub="Liverpool". Nilai masing-masing props kemudian diteruskan ke option props:['klub'].
  4. Di baris kode <a class="nav-link" href="Javascript:void(0)">{{ tab.klub }}</a> saya menuliskan expression {{ tab.klub }}, di bagian ini saya hanya butuh mengakses klub yang ada di option props.
  5. Karena tab merupakan child dari tabs, nilai props dan data di setiap komponen tab dapat saya ambil dari this.$children. Tapi sebelum itu, this.$children harus ditampung terlebih dahulu ke property bertipe array di dalam option data klubSepakBola:[] melalui baris kode this.klubSepakBola = this.$children hook created (baca Lifecycle Hooks Pada Vue.js).
  6. Nilai klub yang diteruskan ke props dapat dibaca menggunakan expression {{ tab.klub }} setelah iterasi collection klubSepakBola dengan directive <li v-for="tab in klubSepakBola">.
contoh komponen tabs vue js

Perpindahan Antar Tab

Setelah link navigasi dan konten setiap tab dibuat, hal yang perlu dipikirkan selanjutnya adalah perpindahan antar tab. Maksudnya, ketika tab Juventus di-klik, maka konten yang ditampilkan hanya paragraf <p></p> berisi para pemain klub terkait : <p>Dybala, Higuain, Ronaldo</p>, hal yang sama berlaku untuk tab Real Madrid dan Liverpool.

Untuk memudahkan Anda memahami konsep perpindahan antar tab, tambahkan option methods berikut pada komponen tabs :

methods: {

    pilihTab(klubDipilih) {

        this.klubSepakBola.forEach(tab => {
            console.log(tab.klub);
        });

        console.log(klubDipilih.pesan + ' : ' + klubDipilih.klub);

        console.log(' ');
    }
    
},

tambahkan juga directive @click="pilihTab(tab)" pada elemen <a class="nav-link" href="Javascript:void(0)">{{ tab.klub }}</a> untuk event handling, diikuti oleh penambahan property pesan:"tab di-klik" pada option data komponen tab.

Vue.component('tabs', {
    template: `
        <div>
            
            <ul class="nav nav-tabs">
                <li v-for="tab in klubSepakBola" class="nav-item">
                    <a 
                        class="nav-link" 
                        href="Javascript:void(0)" 
                        @click="pilihTab(tab)"
                    >{{ tab.klub }}</a>
                </li>
            </ul>

            <div class="tab-content">
                <slot></slot>
            </div>

        </div>
    `,
    data() {
        return { 
            klubSepakBola: [] 
        }
    },
    methods: {

        pilihTab(klubDipilih) {

            this.klubSepakBola.forEach(tab => {
                console.log(tab.klub);
            });

            console.log(klubDipilih.pesan + ' : ' + klubDipilih.klub);

            console.log(' ');
        }
        
    },
    created() {
        this.klubSepakBola = this.$children;
    }
});

Vue.component('tab', {
    props: ['klub'],
    template: `
        <div>
            <slot></slot>
        </div>
    `,
    data() {
        return {
            pesan: "tab di-klik"
        }
    }
});

new Vue ({    
    el: '#app'
});

hasil inspect Console file latihan011.html diperoleh hasil sebagai berikut :

contoh komponen tabs vue js

Ketika tab di-klik, Vue akan meresponnya melalui directive @click="pilihTab(klubDipilih)". Alias klubDipilih hasil iterasi collection klubSepakBola (baris v-for="klubDipilih in klubSepakBola") akan menjadi argumen bagi method pilihTab(klubDipilih). Dari inspect Console, kita bisa mengetahui tab mana yang di-klik

Penanganan klik untuk pindah tab selesai, sekarang tersisa satu masalah terakhir, yaitu bagaimana cara menampilkan konten sesuai tab yang dipilih. Perbarui kembali file latihan011.js dengan baris kode di bawah :

Vue.component('tabs', {
    template: `
        <div>
            
            <ul class="nav nav-tabs">
                <li v-for="klubDipilih in klubSepakBola" class="nav-item">
                    <a 
                        class="nav-link" 
                        href="Javascript:void(0)"  
                        @click="pilihTab(klubDipilih)"
                    >{{ klubDipilih.klub }}</a>
                </li>
            </ul>

            <div class="tab-content">
                <slot></slot>
            </div>

        </div>
    `,
    data() {
        return { 
            klubSepakBola: [] 
        }
    },
    methods: {

        pilihTab(klubDipilih) {

            this.klubSepakBola.forEach(tab => {
                tab.aktif = (tab.klub == klubDipilih.klub);
            });
        }
        
    },
    created() {
        this.klubSepakBola = this.$children;
    }
});

Vue.component('tab', {
    props: ['klub'],
    template: `
        <div v-show="aktif">
            <slot></slot>
        </div>
    `,
    data() {
        return {
            aktif: false
        }
    }
});

new Vue ({    
    el: '#app'
});

Perhatikan baik-baik potongan baris kode komponen tab :

Vue.component('tab', {
    props: ['klub'],
    template: `
        <div v-show="aktif">
            <slot></slot>
        </div>
    `,
    data() {
        return {
            aktif: false
        }
    }
});

Saya cukup melakukan sedikit perubahan pada option template, mengganti <div> menjadi <div v-show="aktif">. Setelah itu, hanya konten tab dengan property aktif bernilai true saja yang dapat ditampilkan. Akan tetapi, karena property aktif secara default bernilai false, maka diperlukan trigger untuk menggantinya agar bernilai true.

Kita lompat ke baris kode di dalam komponen tabs :

methods: {

        pilihTab(klubDipilih) {

            this.klubSepakBola.forEach(tab => {
                tab.aktif = (tab.klub == klubDipilih.klub);
            });
        }
        
    },

Pada potongan kode di atas, setiap kali tab di-klik, directive @click="pilihTab(klubDipilih)" akan memanggil method pilihTab(klubDipilih) yang di dalamnya terdapat baris kode tab.aktif = (tab.klub == klubDipilih.klub). Property aktif akan bernilai true apabila nilai tab.klub == klubDipilih.klub (bandingkan dengan hasil inspect Console pada gambar ke-4).

Parent component tabs dan child component tab kini sudah dapat saling berkomunikasi dan berfungsi tanpa masalah. Hasilnya seperti terlihat pada gambar di bawah :

contoh komponen tabs vue js

Ketika komponen tabs dimuat pertama kali, belum ada tab yang ditampilkan secara default. Memang tidak ada keharusan untuk hal tersebut, tapi … rasanya seperti kurang sreg saja jika tampilan awal tabs kosong / hanya polosan. Selain itu, kita juga masih belum bisa membedakan link navigasi tab yang di-klik dengan yang tidak di-klik (sama-sama berwarna biru).

Menentukan Default Tab dan Warna Link Navigasi yang Dipilih

Rubah baris kode komponen tab menjadi :

Vue.component('tab', {
    props: {
        klub: { required: true },
        dipilih: { default: false }
    },
    template: `
        <div v-show="aktif">
            <slot></slot>
        </div>
    `,
    data() {
        return {
            aktif: false
        }
    },
    mounted() {
        this.aktif = this.dipilih;
        
    }
});

kemudian tentukan tab yang akan dijadikan default pada halaman HTML dengan dipilih=true (misal saya pilih Real Madrid) :

<tab klub="Juventus">
    <p>Dybala, Higuain, Ronaldo</p>
</tab>

<tab klub="Real Madrid" dipilih=true>
    <p>Bale, Benzema, Hazard</p>
</tab>

<tab klub="Liverpool">
    <p>Firmino, Mane, Salah</p>
</tab>

Props dipilih bernilai true pada elemen <tab klub="Real Madrid" dipilih="true"></tab> akan diteruskan ke option props:{ dipilih:{default:false} }. Ketika komponen tab masuk ke hook mounted (baca Lifecycle Hooks Pada Vue.js), nilai property aktif yang semula adalah false return {aktif:false} akan dirubah menjadi true di baris this.aktif=this.dipilih.

Terakhir, tambahkan class binding :class="{'active':klubDipilih.aktif}" agar warna link navigasi tab berubah sesuai class active (bawaan dari css Bootstrap) pada baris kode :

<ul class="nav nav-tabs">
    <li v-for="klubDipilih in klubSepakBola" class="nav-item">
        <a 
            class="nav-link" 
            :class="{ 'active' : klubDipilih.aktif }"
            href="Javascript:void(0)"  
            @click="pilihTab(klubDipilih)"
        >{{ klubDipilih.klub }}</a>
    </li>
</ul>

Jika tidak ada kesalahan, maka hasil akhir file latihan011.html seharusnya akan seperti gambar berikut :

contoh komponen tabs vue js