<div id="app">
  <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
      <a class="navbar-brand" href="#">VueJS</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

      <div class="collapse navbar-collapse" id="navbarsExampleDefault">
        <ul class="navbar-nav mr-auto">
          
          <li class="nav-item"><router-link class="nav-link" :to="{name: 'users'}">Users</router-link></li>
          
           <li class="nav-item"><router-link class="nav-link" :to="{name: 'users2'}">Users2</router-link></li>
          
        </ul>

      </div>
    </nav>

  <!-- route outlet -->
  <!-- component matched by the route will render here -->
  <section class="mainBody">
    <router-view name="content"></router-view>
  </section>

</div>
#app {
  max-width: 800px;
  margin: auto;
  padding: 2rem;
}

.mainBody {
  height: 500px;
  padding-top:50px;
}

.content {
  padding-top: 40px;
}

.list-group-item:hover {
  cursor: pointer;
}
/* =======================================================
      Dummy Data
======================================================= */
const userList = {
  1: {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "rating": 3,
  },
  2: {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "rating": 2,
  },
  3: {
    "id": 3,
    "name": "Clementine Bauch",
    "username": "Samantha",
    "rating": 5,
  }
}

/* =======================================================
      Simple Props and Dynamic Props
======================================================= */

const Child = {
  template: `.
  <div>
    <p>I am the child.</p>
    <p>{{message}}</p>

  </div>`,
  props: {
    message: {
      type: String,
      required: true,
      default: 'This is my default message'
    }
  }
}

const Parent = {
  template: `
  <div class="container">
    <div class="row">
      <div class="col">
        <div><strong>This is our Parent Component </strong></div>
        <div>
          This is the parent content.
        </div>
      </div>
      <div class="col">
        <div><strong>This is our Child Component </strong></div>

          <child-component :message="myMessage">
          </child-component>

      </div>
    </div>
  </div>
`,
  data() {
    return {
      myMessage: undefined
    }
  },
  components: {
    'child-component': Child
  }
}


/* =======================================================
      Child to Parent Communication w/ Custom Events
======================================================= */

const Rating = {
  template: `
    <section class="content">
      <form class="form-inline">
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="onestar" value="1" v-model="userRating">
          <label class="form-check-label" for="onestar">1</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="twostars" value="2" v-model="userRating">
          <label class="form-check-label" for="twostars">2</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="threestars" value="3" v-model="userRating">
          <label class="form-check-label" for="threestars">3</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="fourstars" value="4" v-model="userRating">
          <label class="form-check-label" for="fourstars">4</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="fivestars" value="5" v-model="userRating">
          <label class="form-check-label" for="fivestars">5</label>
        </div>
      </form>
      <button type="button" class="btn btn-dark" style="margin-top: 1rem;" @click="clearRating">Clear Rating</button>
    </section>
  `,
  data() {
    return {
      userRating: null
    }
  },
  props: {
    selectedUserRating: {
      type: Number,
      required: false
    }
  },
  mounted() {
    this.userRating = this.selectedUserRating
  },
  methods: {
    clearRating() {
      this.userRating = null
    }
  },
  watch: {
    userRating: function(newRating, oldRating) {
      // do some async writing to the database, then if successful, emit the update event
      let success = true
  
      if (success) {
        this.$emit('ratingUpdated', newRating)
      }
    }, 
    selectedUserRating: function(newRating, oldRating) {
      this.userRating = newRating
    }
  }
} 

const Users = { 
  template: `

<div class="container">
  <div class="row">
    <div class="col">
      <h2>Users</h2>
      <ul class="list-group" v-if="userList">
        <li class="list-group-item" 
            :class="{active: selectedUser && user.id === selectedUser.id}" 
            v-for="user in userList" 
            @click="setSelectedUser(user)">
          {{user.name}} ({{user.rating}})

        </li>
      </ul>
    </div>
    <div class="col">
      <user-rating v-if="selectedUser" :selectedUserRating="selectedUser.rating" 
        v-on:ratingUpdated="changeDisplayedRating"></user-rating>
    </div>
  </div>
</div>

  `,
  data() {
    return {
      userList: {},
      selectedUser: null
    }
  },
  mounted() {
    this.userList = this.getUsers()
  },
  methods: {
    setSelectedUser(user) {
      this.selectedUser = user
    },
    changeDisplayedRating(val) {
      this.userList[this.selectedUser.id].rating = val
    },
    getUsers() {
      return {... userList}
    }
  },
  components: {
    'user-rating': Rating
  }
}


/* =======================================================
      Child to Parent Communication w/ SYNC Modifier
======================================================= */

const Rating2 = {
  template: `
    <section class="content">
      <form class="form-inline">
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="onestartwo" value="1" v-model="userRating">
          <label class="form-check-label" for="onestartwo">1</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="twostarstwo" value="2" v-model="userRating">
          <label class="form-check-label" for="twostarstwo">2</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="threestarstwo" value="3" v-model="userRating">
          <label class="form-check-label" for="threestarstwo">3</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="fourstarstwo" value="4" v-model="userRating">
          <label class="form-check-label" for="fourstarstwo">4</label>
        </div>
        <div class="form-check form-check-inline">
          <input class="form-check-input" type="radio" id="fivestarstwo" value="5" v-model="userRating">
          <label class="form-check-label" for="fivestarstwo">5</label>
        </div>
      </form>
      <button type="button" class="btn btn-dark" style="margin-top: 1rem;" @click="clearRating">Clear Rating</button>
    </section>
  `,
  data() {
    return {
      userRating: null
    }
  },
  props: {
    selectedUserRating: {
      type: Number,
      required: false
    }
  },
  mounted() {
    this.userRating = this.selectedUserRating
  },
  methods: {
    clearRating() {
      this.userRating = null
    }
  },
  watch: {
    userRating: function(newRating, oldRating) {
      // do some async writing to the database, then if successful, emit the update event
      let success = true
  
      if (success) {
        console.log('updating rating', newRating)
        // this.user.rating = newRating
        this.$emit('update:selectedUserRating', this.userRating)
      }
    }, 
    selectedUserRating: function(selectedUserRating) {
      this.userRating = selectedUserRating
    }
  }
} 

const Users2 = { 
  template: `

<div class="container">
  <div class="row">
    <div class="col">
      <h2>Users 2</h2>
      <ul class="list-group" v-if="userList">
        <li class="list-group-item" 
            :class="{active: selectedUser && user.id === selectedUser.id}" 
            v-for="user in userList" 
            @click="setSelectedUser(user)">
          {{user.name}} ({{user.rating}})
        </li>
      </ul>
    </div>
    <div class="col">
      <user-rating v-if="selectedUser" :selectedUserRating.sync="selectedUser.rating"></user-rating>
    </div>
  </div>
</div>

  `,
  data() {
    return {
      userList: {},
      selectedUser: null
    }
  },
  mounted() {
    this.userList = this.getUsers()
  },
  methods: {
    setSelectedUser(user) {
      this.selectedUser = user
    },
    getUsers() {
      return {... userList}
    }
  },
  components: {
    'user-rating': Rating2
  }
}



/* =======================================================
      Set up our routes. 
      Not important for this example.
======================================================= */

const routes = [
  { path: '/', name: 'home', components: {content: Parent}},
  { path: '/users', name: 'users', components: {content: Users}},
  { path: '/users2', name: 'users2', components: {content: Users2}},
]

const router = new VueRouter({
  routes // short for routes: routes
})

const app = new Vue({
  router
}).$mount("#app")
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta.2/css/bootstrap.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.5.3/vue-router.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
  4. https://getbootstrap.com/assets/js/vendor/popper.min.js
  5. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/js/bootstrap.min.js