Vue.js Rating Selector

September 6, 2019

This article presents a rating selector component in Vue.js. From a variable list of radio buttons, the component communicates a single value back to the caller. Provisions are made for HTML5 and CSS.

The following screenshot shows the RatingSelector component above the horizontal rule. The component displays several radio buttons the number of which is configurable. From there, a selection will notify the parent. In this example, the parent is echoing back the current value.

Rating Selection Reflected In Parent

This is a CodePen demonstrating the RatingSelector.

See the Pen Vue.js Rating Selector Component by Carl Walker (@walkerca) on CodePen.

The RatingSelector component is initialized with two props: initialRating and scale . "scale" determines the number of radio buttons that will be displayed. "initialRating" sets the initially-selected radio button. It can be null for no selection.

The radio buttons are bound to a range iteration. The upper bound is the numeric scale property. The index variable n is used to form HTML id and form attributes that can be used in CSS and assistive readers (the label for attribute). "n" also provides the value that will be transmitted back to the caller.

This v-for range form produces an n that is 1-based. If you also need a 0-based value, you can set the loop to (n,index) in scale and use "index".

The component manages a "rating" data field that is v-model'd to the input control. This is set to the initialRating when mounted. A change to the value, implemented as a watched property, will post an event back to the caller.

const RatingSelector = { props: ["initialRating","scale"], template: ` <div> <span v-for="n in scale"> <input type="radio" name="rb_rating" :id="'rb_' + n" :value="n" v-model="rating" /> <label :for="'rb_' + n">{{ n }}</label> </span> </div> `, data() { return { rating: this.initialRating }; }, watch: { rating() { this.$emit("update-rating", this.rating); } } };

The app instance calls the RatingSelector component using the standard semantics. It's added as a component and referenced in the template (using kebab-style). Two dynamic parameters are passed. The v-bind (shortened to just a colon) is important here as that will allow the v-for loop to treat "scale" as a number. There is an updateRating even handler that will update a property local to the app instance. That's what is displayed below the horizontal rule.

new Vue({ el: "#app", template: ` <div> <h4>Rating Selector Component<h4> <rating-selector :initialRating="rating" :scale="5" @update-rating="updateRating" /> <hr /> <p>Rating: {{ rating }}</p> </div> `, components: { RatingSelector }, data: { rating: 1 }, methods: { updateRating(r) { this.rating = r; } } });

This component presented the user with a list of radio buttons. Pressing one updated the value that was being displayed by the caller. The parameters make this reusable in other scenarios. It's an example where there is an impedance between the UI (5 radio buttons) and the value of iterest (1 value). This case will produce more readable codeby taking the ugliness of iteration out of a parent form and put it into its own component.