3-8 Make your own components

In this lab, we’ll show how to create a Vue component (StarRate.Vue) and use it in the root component of the web app (App.vue).

Properties and one-directional binding

We’d like to create a component called <star-rate> for user to rate something (e.g. products or movie). Take a look at the browser display at the SFC playground for the user interface. The root component App.vue includes the following in its template, and passes values to the <star-rate> component through the two properties: likes and max-likes.

<!-- App.vue includes the child component StarRate.vue -->
<star-rate :likes="4" :max-likes="7"></star-rate>

In StarRate.vue, we define these two properties with the props property in the options object. In this example, the prop likes equals 3, and the prop maxLikes equals 7. Vue will convert the name of the props between kebab-case (in html template) and camelCase (in JavaScript) appropriately. The template can use the props in the same way as data properties. Both work like properties of the view model object.

// StarRate.vue
<template>
  <span class="divStars">
    <img v-for="n in likes" :src="imgStarOn">
    <img v-for="n in (maxLikes-likes)" :src="imgStarOff">
  </span>
</template>

<script>
export default {
  props: {
    likes: Number,
    maxLikes: Number,    
  },

  data() {
    return {
      // these two properties contains URLs to two images
      imgStarOn, imgStarOff, 
    }
  }
}

The code above also demonstrate doing v-for on a range. For example, <span v-for="n in 3">{{n}}</span> will generate three <span> elements in a sequence: <span>1</span><span>2</span><span>3</span>.

If a prop is bound to a data property of the container component, and that data property changes, the prop of the child component also change accordingly, and this will cause the child component to refresh its view. Test this in the SFC playground!

For more information on defining props in components, please read the online reference.

Bi-directional binding and custom events

Passing data into a component through props is one directional. The child component cannot pass updates on the props to the container. In fact, the child component cannot change the value of any prop. In other to inform the container of changes in the child component, we need to define custom events.

Try the example online at SFC playground for what we want to do: A user can click a star in the <star-rate> component to change the rating. This component then emits an event to inform the container component.

To define the custom events, we list the event names in an array in the emits property of the options object. Later, this component can emit the event using this.$emit(eventName, eventData).

// StarRate.vue
<template>
  <span class="divStars">
    <img v-for="n in likes" :src="imgStarOn" 
         @click="clickStar(n)">
    <img v-for="n in (maxLikes-likes)" :src="imgStarOff"
         @click="clickStar(likes+n)">
  </span>
</template>

<script>
export default {
  props: { /* omitted for brevity */ },
  data() { /* omitted for brevity */ },

  emits: [ "update:likes" ],

  methods: {
    clickStar(which) {
      console.log(`click star ${which}`);
      this.$emit("update:likes", which);
    }
  }
}

The event name update:likes is a convention in Vue to inform the container component that the child wants to change the value of the prop likes. This event allows us to do bi-directional binding using v-model as in the following.

<star-rate v-model:likes="current">

In fact, v-model is a syntactical sugar for updating a prop binding via a custom event. The above is the same as the code below.

<star-rate :likes="current" @update:likes="current=$event.target.value">

Similar, v-model="variable" is the same as :modelValue="variable" @update:modelValue="variable=$event.target.value". If you want to use v-model similar to form controls discussed in 3-5 or as in the UI library ElementPlus, call your prop modelValue.

Test the bi-directional binding at the example online at SFC playground.

For more info about defining custom events in components, please read the online reference.

Downloadable examples

  • Version 1 of <star-rate> component example demonstrates properties and one way binding.
  • Version 2 of <star-rate> component example shows custom events and bi-directional binding.
  • Version 3 includes another custom event and demonstrate data binding to class attribute.