VueJS Component Data Communication

Berikut adalah beberapa tips/cara komunikasi data antara parent component dan juga child component pada VueJS(2).

Gunakan ‘props’ attribute

Untuk mengirimkan data dari parent component ke child component menggunakan “props” attribute pada child. Kita dapat mengirimkan simple variable(integer/string atau lainnya) atau complex data seperti array atau object.

Contoh komponen dengan nama “input-tuts”

<template>
    <div>
        Value from parent : {{ dataValue }}
    </div>
</template>

<script>
    export default {
        name: "TutsComponent",
        props : [ 'dataValue' ]
    }
</script>

kita panggil dari parent dengan cara

<input-tuts data-value="This is comes from parent"></input-tuts>

atau

<input-tuts dataValue="This is comes from parent"></input-tuts>

akan menampilkan menghasilkan output sbb:

Catatan: VueJS akan menerjemahkan otomatis apakah kita mau pakai kebab-case atau camel-case utnuk parameternya.

Melakukan modifikasi parameter

Tidak jarang data yang kita kirim ke child component perlu kita proses sebelum kita tampilkan atau print ke layar.

Vue menyarankan data yang dikirim ke child “TIDAK” dirubah, maka dari itu kita akan pasang tiga method tambahan untuk:

  1. Melakukan operasi(misal penambahan)
  2. Melakukan proses ketika data diterima pertama kali.
  3. Melakukan pengawasan/deteksi apabila value yang dikirim berubah

Anggaplah dalam component kita akan setor sebuah nilai(integer) untuk dikalikan dengan 10 lalu kita tampilkan ke layar. Pastinya komponent harus siap apabila value melakukan perubahan karena data binding(v-bind).

Child Component kita akan ubah seperti berikut:

<template>
    <div>
        Value from parent : {{ finalValue }}
    </div>
</template>

<script>
    export default {
        name: "TutsComponent",
        props : [ 'dataValue' ],
        data(){
            return {
                finalValue : 0,
            }
        },
        methods : {
            doSomething(){
                this.finalValue = 10 * this.dataValue;
            }
        },
        watch : {
            dataValue(){
                /**
                 * Method ini berguna untuk berjaga-jaga apabila parameter yang dikirim
                 * dinamis, dan berubah sewaktu-waktu. Sehingga ketika terjadi eprubahan value
                 * pada parameter dataValue, kita panggil kembali method prosesor untuk melakukan
                 * kalkulasi ulang.
                 * Tanpa method ini, ketika value "dataValue" berubah(karena data dari binding)
                 * proses kalkulasi tidak akan dijalankan lagi.
                 */
                this.doSomething();
            }
        },
        mounted() {
            /**
             * Method ini akan dieksekusi ketika komponent di eksekusi.
             * kita langsung meminta component untuk melakukan operasi
             * pengalian untuk value yang diterima pada method prosesor.
             */
            this.doSomething();
        }
    }
</script>

Sekarang kita panggil dari parent component, hanya saja valuenya kita ganti seperti berikut:

<template>
    <div>
        <input-tuts :dataValue="4"></input-tuts>
    </div>
</template>

<script>
    export default {
        name: "TutsParentComp"
    }
</script>

Saat dijalankan ke browser:

Okay, berikutnya kita akan buat value yang dikirimkan dinamis(kita bisa ubah). Untuk itu kita ubah parent component yang memanggilnya:

<template>
    <div>
        <input type="text" v-model="inputNum">
        <input-tuts :dataValue="inputNum"></input-tuts>
    </div>
</template>

<script>
    export default {
        name: "TutsParentComp",
        data() {
            return {
                inputNum : 4
            }
        }
    }
</script>

Dengan cara diatas maka value “inputNum” berasal dari textbox yang menggunakan two-way data binding. sehingga ketika kita ganti value di textbox akan ter-refleksikan pada child element juga.

Membuat Custom Event

Custom event salah satu gunanya adalah selain melakukan triger event kita juga bisa menggunakan untuk melakukan passing kembali value dari child ke parent.

Event tidak melulu standard event(change, press, etc) tapi kita bisa membuat custom event name(misal: changed, etc)

Untuk contoh ini kita buat child element menerima input dengan menggunakan dropdown(select).

Child component:

<template>
    <div>
        <select v-model="dataValue">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
    </div>
</template>

<script>
    export default {
        name: "TutsComponent",
        props : [ 'dataValue' ],
        data(){
            return {}
        },
        watch : {
            dataValue() {
                this.$emit('change', this.dataValue);
            }
        }
    }
</script>

Parent:

<template>
<div>
<input-tuts :dataValue="inputNum" @change="childChangeEvent($event)"></input-tuts>
<div>Selected value : {{ inputNum }}</div>
</div>
</template>

<script>
export default {
name: "TutsParentComp",
data() {
return {
inputNum : 2
}
},
methods : {
childChangeEvent(data){
this.inputNum = data;
}
}
}
</script>

Nah kalau kita jalankan, saat kita ganti dropdown ke value lain, pada parent value juga berganti. Kenapa bisa?

kalau kita lihat di child component, kita pasang watcher untuk variable “dataValue” dan ketika terjadi perubahan, child akan menjalankan event “change” dan juga mengirimkan value barunya.

Pada parent, component kita tangkap dengan binding event

@change="childChangeEvent($event)"

Yang isinya:

childChangeEvent(data){
     this.inputNum = data;
}

Jadi intinya, ketika value dirubah di child, event akan dijalankan, ditangkap oleh fungsi yang kita berikan di callernya.

TAPIIII…… VueJS tidak menyarankan mengubah data yang direferensikan di prop.

Vue JS akan memberikan error sbb:

Lalu bagaimana mengatasinya? Kita buat beberapa perbaikan di child component menjadi:

<template>
    <div>
        <select v-model="localValueDropdown">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
    </div>
</template>

<script>
    export default {
        name: "TutsComponent",
        props : [ 'dataValue' ],
        data(){
            return {
                localValueDropdown : 0,
            }
        },
        methods: {
            setValue(){
                this.localValueDropdown = this.dataValue;
            }
        },
        watch : {
            dataValue() {
                this.setValue();
            },
            localValueDropdown(){
                this.$emit('change', this.localValueDropdown);
            },
        },
        mounted() {
            this.setValue();
        }
    }
</script>

Pada code diatas, kita pindahkan isi variable dataValue which is dari parent ke variable lainnya yaitu localValueDropdown. Perubahan two-way binding dropdown di child component juga mengacu ke localValueDropdown bukan ke properti binding lagi.

Tetapi karena kita harus begini, ada beberapa method yang harus kita tambahkan untuk memantau isi variable yang dikirim dari parent.

Done… no more error 🙂

2-way Binding with “v-model”

Nah sebenarnya sih kalau secara fungsional sudah cukup mewakili apabila kita bisa kirim value ke child dan data bisa ditransfer kembali kepada parent ketika terjadi perubahan data pada child dengan cara di atas.

Nah tapi kita pengen lebih simple lagi, yaitu dengan menggunakan v-model. Sehingga kita tidak perlu buat method tambahan untuk menangkap value yang dikirimkan oleh childnya.

Untuk menggunakan v-model ada 2 hal yang kita butuh lakukan:

  1. Mengubah prop pada child component menjadi “value
  2. Mengubah event name-nya dari change menjadi “input

Sehingga child component menjadi:

<template>
    <div>
        <select v-model="localValueDropdown">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
    </div>
</template>

<script>
    export default {
        name: "TutsComponent",
        props : [ 'value' ],
        data(){
            return {
                localValueDropdown : 0,
            }
        },
        methods: {
            setValue(){
                this.localValueDropdown = this.value;
            }
        },
        watch : {
            dataValue() {
                this.setValue();
            },
            localValueDropdown(){
                this.$emit('input', this.localValueDropdown);
            },
        },
        mounted() {
            this.setValue();
        }
    }
</script>

Pada parent, cara panggilnya kita persingkat menjadi:

<template>
    <div>
        <input-tuts v-model="inputNum"></input-tuts>
        <div>Selected value : {{ inputNum }}</div>
    </div>
</template>

<script>
    export default {
        name: "TutsParentComp",
        data() {
            return {
                inputNum : 2
            }
        }
    }
</script>

Kesimpulan

Pada artikel kali ini kita sudah dapat :

  1. Melakukan pengiriman data dari parent ke child component.
  2. Melakukan trigger event dari child ke parent component (serta mengirim data event)
  3. Meringkas pengiriman balik data dari child ke parent hasil event dengan menggunakan v-model.