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).
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.
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 usev-model
similar to form controls discussed in 3-5 or as in the UI library ElementPlus, call your propmodelValue
.
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.
<star-rate>
component example demonstrates properties and one way binding.
<star-rate>
component example shows custom events and bi-directional binding.
class
attribute.