Di materi ini, saya akan menggabungkan hampir semua hal yang pernah saya bahas di materi-materi sebelumnya, mulai dari penggunaan directive @click dan v-show, dasar pembuatan komponen, pembuatan child component disertai slot, hingga interaksi parent-child dengan props dan $emit. Komunikasi komponen dilakukan antara parent Card dengan child Modal. Agar lebih menarik, keduanya saya buat dalam bentuk Collapsible (dapat ditampilkan dan disembunyikan dengan klik button).

Mungkin akan terasa sedikit sulit, tapi jangan khawatir, sebab saya akan menjelaskannya langkah demi langkah, dimulai dari yang paling mudah. Sekarang kita mulai dengan membuat file latihan010.html, lalu copy paste baris kode berikut :

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Latihan Vue 010</title>
    <link rel="stylesheet" href="bootstrap.min.css">
</head>
<body>
    <div id="app">
        <div class="jumbotron text-center">
            <h1>Bootstrap 4</h1>
            <p>Latihan010 - Komunikasi Komponen Vue.js Collapsible Card dan Modal</p>
          </div>
        <div class="container">
            <div class="row">
                <div class="col-md-6">

                    <komponen-card nama="Varian Hermianto" whatsapp="08-222-666-777-8">
                    Orang Makassar yang sudah jatuh hati dan betah tinggal di Jogja. 
                    </komponen-card>

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

dan kode berikut ke file latihan010.js :

Vue.component('KomponenCard', {
    props: ['nama', 'whatsapp'],
    template: `
        <div style="margin-top: 10px; margin-bottom: 30px;">
            <div class="card" style="width:300px;">
                <img class="card-img-top" src="img/img_avatar1.png" style="width:100%">
                <div class="card-body">
                    <h4 class="card-title">{{ nama }}</h4>
                    <p class="card-text"><slot></slot></p>
                    <button type="button" class="btn btn-primary">Tampilkan Modal</button>
                </div>
            </div>
        </div>
    `
});

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

Pada kode di atas, saya baru memiliki satu buah komponen Vue bernama KomponenCard yang dapat diakses melalui elemen <komponen-card></komponen-card> di halaman HTML. Di dalam <komponen-card></komponen-card> tersebut, saya menyisipkan dua props dan satu slot. Dua props masing-masing adalah nama="Varian Hermianto" dan whatsapp="08-222-666-777-8", sedangkan <slot></slot> diisi dengan Orang Makassar yang sudah jatuh hati dan betah tinggal di Jogja.

<komponen-card nama="Varian Hermianto" whatsapp="08-222-666-777-8">
Orang Makassar yang sudah jatuh hati dan betah tinggal di Jogja. 
</komponen-card>

Di sisi Vue, props dibaca melalui baris kode props:['nama','whatsapp'] dan slot pada baris kode <p class="card-text"><slot></slot></p>. Karana berada di dalam option template, property nama pada props tetap dapat diakses tanpa keyword this, lihat expression <h4 class="card-title">{{ nama }}</h4>.

Hasil eksekusi file latihan010.html :

komunikasi komponen vue.js collapsible card dan modal

Collapsible Card Sebagai Parent Component

Masuk ke bagian collapsible, di sini saya akan membuat KomponenCard dapat ditampilkan dan disembunyikan dengan klik button. Rubah baris kode berikut pada file latihan010.html :

<div class="container">
    <div class="row">
        <div class="col-md-6">

            <komponen-card nama="Varian Hermianto" whatsapp="08-222-666-777-8">
            Orang Makassar yang sudah jatuh hati dan betah tinggal di Jogja. 
            </komponen-card>

        </div>
    </div>
</div>

menjadi :

<div class="container">
    <div class="row">
        <div class="col-md-6">

            <button 
                type="button"
                class="btn btn-primary"
                @click="showCard = !showCard"
            >{{ showCard ? "Tutup Card" : "Tampilkan Card" }}</button>

            <komponen-card v-show="showCard" nama="Varian Hermianto" whatsapp="08-222-666-777-8">
            Orang Makassar yang sudah jatuh hati dan betah tinggal di Jogja. 
            </komponen-card>

        </div>
    </div>
</div>

dan baris kode pada file latihan010.js :

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

menjadi :

new Vue ({    
    el: '#app',
    data: {
        showCard: false 
    }
});

Sekarang KomponenCard hanya akan ditampilkan jika property showCard bernilai true. Nilai showCard dapat dirubah dari true ke false dan dari false ke true melalui directive @click="showCard = !showCard". Kemudian, label teks pada button juga dapat diganti secara dinamis dengan {{ showCard?"Tutup Card":"Tampilkan Card" }}. Jika property showCard bernilai true, label teks yang muncul adalah Tutup Card, sebaliknya bila false, maka yang muncul adalah Tampilkan Card.

komunikasi komponen vue.js collapsible card dan modal

Modal Sebagai Child Component

Lanjut ke pembuatan child component, kali ini kita akan menambahkan Modal sebagai child component dari Card. Pastikan, bahwa KomponenModal hanya dapat digunakan saat KomponenCard tidak dalam keadaan display:none; atau <komponen-card v-show="showCard" bernilai true.

Perbarui file latihan010.js, copy paste kode di bawah :

Vue.component('KomponenCard', {
    props: ['nama', 'whatsapp'],
    template: `
        <div style="margin-top: 20px;">
            <div class="card" style="width:400px">
                <img class="card-img-top" src="img/img_avatar1.png" style="width:100%">
                <div class="card-body">
                <h4 class="card-title">{{ nama }}</h4>
                <p class="card-text"><slot></slot></p>
                <button type="button" class="btn btn-primary" @click="tampilkanModal()">Tampilkan Modal</button>
                </div>
            </div>
            <komponen-modal :title=nama :body=whatsapp @tutup="displayModal = 'none'" :displayStatus=displayModal></komponen-modal>
        </div>
    `,
    data() {
        return {
            displayModal: 'none'
        }
    },
    methods: {
        tampilkanModal() {
            this.displayModal = 'block';
        }
    }
});

Vue.component('KomponenModal', {
    props: {
        title: '',
        body: '',
        displayStatus: {
            default: 'none' 
        }
    },
    template: `
        <div class="modal fade show" :style="{ display:displayStatus }">
            <div class="modal-dialog modal-dialog-centered">
                <div class="modal-content">
                
                    <div class="modal-header">
                        <h4 class="modal-title">{{ title }}</h4>
                        <button type="button" class="close" @click="$emit('tutup')">×</button>
                    </div>
                    
                    <div class="modal-body">
                        Saya dapat dihubungi via WA {{ body }}
                    </div>
                    
                    <div class="modal-footer">
                        <button type="button" class="btn btn-primary" @click="$emit('tutup')">Tutup Modal</button>
                    </div>
                    
                </div>
            </div>
        </div>     
    `
});
new Vue ({    
    el: '#app',
    data: {
        showCard: false 
    }
});

Secara default, KomponenModal tidak akan ditampilkan karena displayModal:'none'. Agar Modal dapat ditampilkan, nilai property displayModal harus dirubah terlebih dahulu dari 'none' ke 'block' di baris this.displayModal='block' method tampilkanModal(). Method tampilkanModal() sendiri dapat dipanggil melalui directive @click="tampilkanModal()".

<button type="button" class="btn btn-primary" @click="tampilkanModal()">Tampilkan Modal</button>

Pemanggilan child KomponenModal dilakukan dari template milik KomponenCard yang bertindak sebagai parent pada baris kode :

<komponen-modal :title=nama :body=whatsapp @tutup="displayModal = 'none'" :displayStatus=displayModal></komponen-modal>

Ketika memanggil KomponenModal melalui elemen <komponen-modal></<komponen-modal>, saya menyisipkan tiga props berbeda, yaitu :displayStatus=displayModal, :title=nama dan :body=whatsapp. Penulisan props pada elemen <komponen-modal></komponen-modal> wajib disertai awalan : karena yang diteruskan berupa variabel. Sedangkan props pada elemen <komponen-card></komponen-card>, karena nilai yang diteruskan bukan variabel (sudah berbentuk string), maka awalan : tidak perlu ditulis lagi. Lah koq harus begitu ? ya memang begitu sih aturan dari sana-nya 🙂

<div class="modal-footer">
    <button type="button" class="btn btn-primary" @click="$emit('tutup')">Tutup Modal</button>
</div>

Directive @tutup="displayModal='none'" digunakan untuk menerima event yang dikirim oleh $emit dari template KomponenModal. Ketika event tutup dari child KomponenModal diterima oleh parent KomponenCard, nilai property displayModal akan berubah menjadi 'none' dari yang sebelumnya 'block' di baris displayModal='none'. Nilai dari displayModal kembali digunakan di :displayStatus=displayModal lalu diteruskan menjadi style binding :style="{ display:displayStatus }" di template child KomponenModal.

komunikasi komponen vue.js collapsible card dan modal

Dari gambar di atas, sebenarnya Modal sudah dapat ditampilkan dengan baik, hanya saja masih ada sedikit yang kurang, bagaimana kalau kita buat agar ketika Modal tampil, warna latar di belakangnya menjadi lebih gelap ? Caranya, sisipkan satu baris kode berikut pada file latihan010.js :

<div class="modal-backdrop fade show" v-show="displayModal == 'block'"></div>

tepat di bawah :

<komponen-modal :title=nama :body=whatsapp @tutup="displayModal = 'none'" :displayStatus=displayModal></komponen-modal>

Hasil akhirnya menjadi seperti gambar di bawah ini :

komunikasi komponen vue.js collapsible card dan modal