1904195
Having finished with the getting started video at vuejs.org, I began the next exploration with the free Vue Mastery introductory course containing 11 lessons, each a downloadable video. In the earlier post I summarized the first seven lessons. This is the concluding part where I summarize the last four lessons.
Lesson 8 - Components
The lesson transformed the product div portion of the application (and applicable sections of the Vue instance declaration) to a component so that it got replaced with <product :premium="premium"></product> in the HTML just like it’s done in React. Unlike in React, components are not created as classes. The following shows the structure of a component:
Vue.component("componentID",{
  props: propsDefinition,
  template: templateDefinition,
  data(){ return dataObject },
  methods: methodsObject,
  computed: computedObject
 })
The componentID can be any value not containing space apart from a built-in HTML attribute. It is what will be placed inside <> to render the component. The propsDefinition is an object containing one or more variables such as {premium:{type: Boolean, required: true } } and defines the arguments to be passed to the component, in this case one named premium. The template property is the only compulsory one in the component options object. It is the one that defines the HTML string to be rendered by the component. Its one requirement is only one root-level HTML element can be returned. This means "<p>First paragraph</p><p>Second paragraph</p>" can’t be returned because there are two <p> elements, so the solution would be to put them in a container, as in "<div><p>First paragraph</p><p>Second paragraph</p></div>" and now there is only one root <div> being returned. There are different methods of creating a template such as multi-line string or single-line string, quoted with back-tick, or single- or double-quote characters as the need may be. This Medium article listed seven methods. The dataObject, methodsObject and computedObject values are just like in the Vue instance declaration.
Lesson 9 - Communicating events
When a component needs to let the parent know of an event within it, it needs to $emit the event like this: this.$emit("customeventname",eventparam) At the point the component is called, the event will need to be listened for like this: <Component @customeventname="handler"> Here, handler could be a method and if the event would be emitted with the optional eventparam then the method would be declared with an argument.
Lesson 10 - Forms
A product-review component was added to the app, within the product component, with the following form features:
- @submit.prevent="onSubmit" The .prevent modifier stops the default behavior of page re-display when the form is submitted. The onSubmit method collects the values on the form into an object and then emits a review-submitted event with it. The event handler adds the data to an array for the product component, which is then listed out in a “Reviews” section before the reviews form. onSubmit also handles custom validation, checking to see that each field was supplied.
 - v-model="review" Each HTML input element is bound to a Vue variable with v-model so that a change in the input would reflect in the variable (review in this case) and vice versa. v-model.number ensures that the returned value is a number, and also introduces increment/decrement arrow buttons for a text input box.
 - Custom error display. The required attribute for HTML input elements was bypassed. An errors array was added to the data object of the component and is populated by the onSubmit method. At the top of the reviews form if the array is not empty its contents are displayed.
 
Lesson 11 - Tabs
A product-tabs component was added to the app, within the product component, while the product-reviews component was moved from product to it. It’s basically a menu bar inside a div with two options “Reviews” and “Make a Review” followed by two elements, one for each menu item, only one showing at a time (via v-show), the one corresponding to the currently-selected menu item. The reviews array declared for the product component now had to be passed to the product-tabs as an argument. But the addReview() method of the product component no longer worked as product no longer referenced the product-reviews directly. My first solution was to just transfer it from the product component to product-tabs and this worked, probably because arrays were passed as arguments by reference, and values in a component’s props can also be prefixed with this just like data values.
However, the lesson author’s solution turned out to be different as he said a grandchild cannot emit an event for a grandparent to listen to. What he did was to
- Remove the listening for the review-submitted event from where product-reviews was called within product-tabs.
 - Declare a new global Vue instance called eventBus with no parameters:
var eventBus=new Vue() - Use eventBus within the onSubmit method of the product-reviews component, to emit the event, rather than using this:
eventBus.$emit("review-submitted",productReview) - Remove the addReview method from product component.
 - Introduce the mounted() lifecycle method for the product component, which now used eventBus to listen to the review-submitted event and then do exactly what addReview() used to do:
mounted(){
eventBus.$on("review-submitted",productReview=>{this.reviews.push(productReview)})
} 
By the conclusion of this lesson, and the Vue Mastery introductory course, the course application now looks like this (see above).
And the Vue instance declaration looked like this, with most of the code from the end of lesson 7 having been moved out to components:
var app=new Vue({
  el: "#app",
  data:{premium:true, cart: [] },
  methods:{updateCart(id){this.cart.push(id)} }
 })