Custom Event from Child Component to Parent

Vue Custom Event from Child Component to Parent demo by http://qcode.in

by Saqueib Ansari

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
<div class="container">
  <div id="app">
    <user-list></user-list>
  </div>
</div>

<!-- User List Template -->
<template id="user-list">
  <div class="panel panel-default">
    <div class="panel-heading">List of Users</div>
    <div class="panel-body">
      <div v-if="loading" class="text-center">
        <span class="label label-info">Loading Users...</span>
      </div>

      <div class="user-list">
        <div v-for="user in users" class="media">
          <div class="media-left">
            <a href="#">
              <img width="48" class="media-object img-circle" :src="user.avatar" :alt="user.first_name">
            </a>
          </div>
          <div class="media-body">
            <h4 class="media-heading">{{ user.first_name + ' ' + user.last_name }}</h4>
            <follow @followed="handleFollowed" :user="user"></follow>
            <!-- child component -->
          </div>
        </div>
      </div>
      <!-- end user list -->
    </div>
  </div>
</template>

<!-- Follow Template -->
<template id="follow">
  <a href="#" @click.prevent="toggle" class="btn btn-xs" :class="{'btn-success': isFollowed, 'btn-default': !isFollowed}">
    {{ isFollowed ? 'Un-Follow' : 'Follow' }}
 </a>
</template>

CSS

body {
  margin-top: 1rem
}

.user-list .media-heading {
  text-transform: capitalize;
}

JavaScript

// User List Component 
Vue.component('user-list', {
  template: '#user-list',
  data: function() {
    return {
      loading: false,
      users: []
    }
  },
  methods: {
    handleFollowed: function(payload) {
      console.log(payload);
      let followed = payload.followed ? 'followed' : 'un followed';
      alert(payload.user.first_name + " " + payload.user.last_name + " was " + followed);
    }
  },
  created: function() {
    // fetch some dummy user on create
    let vm = this;
    vm.loading = true;

    axios.get('https://reqres.in/api/users?per_page=10').then(function(res) {
      vm.users = res.data.data
      console.log('users loaded');
      vm.loading = false;
    })
  }
});

// Follow Component 
Vue.component('follow', {
  template: '#follow',
  props: ['followed', 'user'],
  data: function() {
    return {
      isFollowed: this.followed
    }
  },
  methods: {
    toggle: function() {
      this.isFollowed = !this.isFollowed;

      // emit event
      this.$emit('followed', {
        user: this.user,
        followed: this.isFollowed
      });
    }
  }
});

// Initialize Vue
new Vue({
  el: '#app'
})