Komunikasi Antara Dua Komponen VueJS di Laravel


Menggunakan VueJS di Laravel sudah begitu dimudahkan. Tim pengembang sudah menyediakan librari dan instance VueJS dalam aplikasi, disertakan juga contoh komponen, dokumentasi pembuatan, serta tata cara kompilasinya.

Laravel merekomendasikan kita untuk menggunakan VueJS dalam bentuk komponen-komponen. Komponen ini nantinya akan dipanggil pada view (Blade) sesuai dengan kebutuhan sistem. Itulah kenapa, dalam berkas resources/assets/js/app.js sudah tersedia root instance VueJS.

Awal-awal belajar VueJS dan mengimplementasikannya di Laravel, saya menghapus instance yang ada di berkas app.js dan membuat instance pada masing-masing view (Blade), di mana ada proses menggunakan VueJS. Kendalanya cuma satu, saya belum bisa mengkomunikasikan dua komponen yang berbeda.

Semisal, saya memiliki tiga buah komponen VueJS, di antaranya: search.vue, user.vue (menampilkan data user), dan pagination.vue. Walaupun berada pada komponen yang berbeda, seharusnya datanya saling berkaitan. Semisal, saya melakukan aksi pencarian pada search.vue, maka akan memicu perubahan data pada user.vue. Begitu seterusnya satu aksi memicu aksi lainnya di komponen lain.

Bukankah lebih gampang membuatnya jadi satu komponen?

Iya betul, tapi konsep utama sebuah komponen bukanlah begitu. Komponen dibuat terpisah karena fungsinya yang re-usability — dapat digunakan kembali — . Komponen search.vue dan pagination.vue misalnya, semestinya dapat saya gunakan kembali pada view atau data yang berbeda.

Nah, untuk kasus di atas, ada dua cara untuk menanganinya. Kedua cara tersebut juga sudah dijelaskan dalam dokumentasi VueJS.

Cara pertama lebih sederhana. Kalau manajemen data tidak terlalu kompleks, maka gunakan cara tersebut. Cara inilah yang akan saya jabarkan pada tulisan ini.

Cara kedua jauh lebih kompleks dan butuh librari tambahan, yaitu Vuex. Bagi yang familiar dengan Facebook Flux atau Redux, konsep yang diusung Vuex adalah serupa.

Membuat Komponen

Terdapat tiga buah komponen VueJS yang akan dibuat, berturut-turut akan saya tampilkan pada tulisan ini, namun tidak akan saya jelaskan secara rinci. Skrip lengkap bisa kalian baca sepenuhnya pada contoh aplikasi.

Bagi yang belum begitu paham dengan komponen VueJS, silakan baca lagi di halaman ini.

Komponen search.vue

<template>
  <form @submit.prevent="search" method="post">
    <div class="form-group">
      <label>Keyword</label>
      <input v-model="state.keyword" type="text" class="form-control">
    </div>

    <div class="form-group">
      <button type="submit" class="btn btn-primary">Search</button>
    </div>
  </form>
</template>

<script>
  export default {
    props: {
      limit: {
        type: Number,
        default: 20
      }
    },

    data() {
      return {
        state: {
          keyword: ''
        }
      }
    },

    methods: {
      search() {
        this.$bus.$emit('search', this.state.keyword);
      }
    }
  }
</script>

Komponen user.vue

<template>
  <table class="table">
    <thead>
      <tr>
        <th>#</th>
        <th>Email Address</th>
        <th>Crated</th>
        <th>Actions</th>
      </tr>
    </thead>

    <tbody>
      <tr v-for="(user, index) in users.data">
        <td>{{ users.from + index }}</td>
        <td>{{ user.email }}</td>
        <td>{{ user.created_at }}</td>
        <td>#</td>
      </tr>
    </tbody>
  </table>
</template>

<script>
  export default {
    data() {
      return {
        users: []
      }
    },

    mounted() {
      const url = '/user/paginate';

      this.getUser(url);

      // listen when search
      this.$bus.$on('search', keyword => {
        let params = {
          keyword: keyword
        }

        this.getUser(url, params);
      });

      // listen when pagination clicked
      this.$bus.$on('loadPagination', url => {
        this.getUser(url);
      });
    },

    methods: {
      getUser(url, params = null) {
        axios.get(url, {params}).then(response => {
          this.users = response.data;

          this.$bus.$emit('pagination', this.users);
        })
      }
    }
  }
</script>

Komponen pagination.vue

<template>
  <ul class="pagination">
    <li v-if="pagination.prev_page_url">
      <a @click.prevent="load(pagination.prev_page_url)" :href="pagination.prev_page_url">
        &laquo; Previous
      </a>
    </li>

    <li v-if="pagination.next_page_url">
      <a @click.prevent="load(pagination.next_page_url)" :href="pagination.next_page_url">
        Next &raquo;
      </a>
    </li>
  </ul>
</template>

<script>
  export default {
    data() {
      return {
        pagination: []
      }
    },

    mounted() {
      this.$bus.$on('pagination', data => {
        this.pagination = data;
      });
    },

    methods: {
      load(url) {
        this.$bus.$emit('loadPagination', url);
      }
    }
  }
</script>

Komunikasi Beberapa Komponen (Non Child-Parent)

Di dokumentasinya, sudah dijelaskan untuk komunikasi dua buah komponen, kita memerlukan sebuah “bus”. Bus berperan sebagai jembatan antar beberapa komponen tersebut. Tentunya, bus hanya sebuah konsep dan istilah yang digunakan sebagai perantara, kalian bebas memberinya nama apa saja.

Bus merupakan instance kosong dari VueJS yang harus ditampung ke dalam sebuah variabel. Variabel ini nantinya akan digunakan pada instance utama VueJS dalam aplikasi kita.

window.bus = new Vue();

Contoh skrip lengkapnya dapat dilihat pada media berikut:

/*
 * By extending the Vue prototype with a new '$bus' property
 * we can easily access our global event bus from any child component.
 *
 * @link https://laracasts.com/discuss/channels/vue/use-a-global-event-bus
 */
Object.defineProperty(Vue.prototype, '$bus', {
    get() {
        return this.$root.bus;
    }
});

window.bus = new Vue();

window.app = new Vue({
  el: '#app',
  data: {
    bus: bus // Here we bind our event bus to our $root Vue model.
  }
});

Emitter dan Listener

Bus beserta prototipenya sudah dibuat. Langkah selanjutnya adalah menentukan emitter dan listener.

Sederhananya, emitter merupakan sebuah tugas yang berfungsi untuk memicu sebuah event. Sedangkan listener merupakan sebuah tugas yang berfungsi menangkap event tersebuh dan menjalankan tugas selanjutnya.

Contoh sederhana berdasarkan pengaturan di atas, dapat ditulis sebagai berikut.

// emitter menggunakan bus
this.$bus.$emit('event', params);
// listener menggunakan bus
this.$bus.$on('event', params => {
   // do something here
});

Semisal, ketika kita melakukan pencarian pada kompononen search.vue maka akan memicu sebuah event “pencarian” — akan diberi nama search — . Kemudian, komponen user.vue mempunyai peran untuk mendengarkan event tersebut untuk melakukan proses pencarian user berdasarkan katakunci yang dikirim dari komponen search.vue.

Berikut contoh implementasi sederhananya:

methods: {
  search() {
    this.$bus.$emit('search', this.state.keyword);
  }
}

Di komponen user.vue juga ditambahkan listener untuk menangkap event dari search.

// listen when search
this.$bus.$on('search', keyword => {
  let params = {
    keyword: keyword
  }
  this.getUser(url, params);
});

Dapat dilihat juga pada method getUser() — lihat pada contoh skrip lengkap di atas — , memicu sebuah event dengan nama pagination. Dan, event ini akan diproses pada komponen pagination.vue. Begitu seterusnya sebuah event dikirim dari satu komponen ke komponen lainnya.

Contoh Aplikasi

Seperti dengan tulisan sebelumnya, tidak lupa juga saya sertakan repositori aplikasi yang dapat kalian pergunakan sebagai bahan pembelajaran.

laravel-id/non-child-vue-components

non-child-vue-components – Komunikasi dua komponen VueJS (Non-child components).

Demo aplikasi juga bisa kalian coba pada tautan berikut.

Jangan sungkan untuk bertanya apabila kalian mengalami kendala dalam mencoba. Tak lupa, segera laporkan jika ada beberapa bagian yang cukup sulit untuk dipahami.

Terakhir, selamat belajar dan mencoba. Jangan lupa dibagikan jika bermanfaat! 😉

Tak Berkategori

Yugo Purwanto

Pemrogram PHP dan JavaScript yang sedang sibuk mengembangkan aplikasi Glosarium Bahasa Indonesia.

Tinggalkan Balasan