Chapter 6. Working with components

This chapter covers

  • Understanding parent and child relationships in components
  • Learning about local and global registration
  • Using props and prop validation
  • Adding custom events

In the previous chapters, we learned about conditionals, looping, and lists. Instead of repeating code, we used loops to simplify things. We used conditionals to show different messages depending on the user action. This works, but you may have noticed that our application has grown to more than 300 lines of code. The index.html file we’ve been updating every chapter has computed properties, filters, methods, lifecycle hooks, and data properties in it. With all this information, it’s not easy finding things.

To help solve this, we need to separate our code and componentize it. Each part of the code should be reusable and allow properties and events to be passed to it.

Vue.js components can help us accomplish this. Before we begin, let’s look at several of the fundamentals of components and a few examples of how they work. Then we’ll look at local and global registration of components. Afterward, we’ll see a few examples on how to pass props and how to validate them. We’ll end the chapter with defining our templates and custom events.

You maybe be wondering what happened to our pet store application. Don’t worry, we’ll look at this in the next chapter when we look at single-file components, build tools, and Vue-CLI.

6.1. What are components?

Components in Vue.js are a powerful construct that reduce and simplify our code base. Most Vue.js apps consist of one or more components. With components, we can pull out repeated parts of our code and separate them into smaller logical parts that make more sense to us. We can reuse each component throughout our application. Components are defined as a collection of elements that can be accessed through a single element. In certain cases, they can appear as a native HTML element using a special is attribute (we’ll look at that operator later in this chapter).

Figure 6.1 is a simple example of converting a few HTML tags into one component. All of the HTML inside the opening and closing <div> tags are encapsulated in the one component: my-component. It’s worth mentioning that you can also have a self-closing tag, <my-component/> if your web browser supports it or you’re using single-file components that we’ll discuss later in the chapter.

Figure 6.1. Example of encapsulating code into a component

6.1.1. Creating components

Before we can create our first Vue component we must create a Vue.js root instance, and then we must decide how to structure our application. Vue.js gives us the option to register our components either locally or globally. Global components can be used throughout the application, while local components can only be used in the Vue.js instance that created it. Let’s create a global component first.

Global components, as we mentioned earlier, can be used in all Vue.js instances. In this example, we’re creating a global component called my-component. Vue.js allows much flexibility in naming our components. Keep in mind that Vue.js doesn’t enforce any rules for component names, the way other frameworks do. It’s good practice to name all your components lowercase with hyphens.

6.1.2. Global registration

To create a global component, we must place it before the Vue instance. As you can see from listing 6.1, the global component (my-component) is defined right before it’s created with new Vue.

To display information in our component, we must add a template property. The template is where the HTML resides. Keep in mind that all templates must be surrounded by a tag. In our example, we’ve surrounded it in a <div> tag; otherwise, you’ll see an error in the console, and the template won’t render on the screen.

The last thing we need to do to get our component to render is to add it to the parent’s template. To get this working, we add the <my-component></mycomponent> custom tag inside the parent entry point of our app, <div id="app">, as shown in the following listing. While going through this chapter, try these examples for yourself. Make sure to save them as a .HTML file and load them in your web browser.

Listing 6.1. Creating our first global component chapter-06/global-component-example.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>            1
</head>
  <body>
  <div id="app">
    <my-component></my-component>                        2
  </div>
  <script>
  Vue.component('my-component', {                        3
    template: '<div>Hello From Global Component</div>'   4
  });

  new Vue({                                              5
    el: "#app"
  });
  </script>
  </body>
</html>

  • 1 Adds script tag to Vue.js
  • 2 Adds component to template
  • 3 Registers global Vue to component
  • 4 Renders template for component
  • 5 Instantiates Vue instance

It goes without saying that our application isn’t too useful. If you open this file in a web browser, you’ll see a message on the page, “Hello From Global Component.” This is a trivial example on how components work so you can understand the fundamentals. Let’s look at local registration and see how that differs.

The is special attribute

You have special restrictions when using components in our DOM. Certain HTML tags—<ul>, <ol>, <table>, and <select>—have restrictions on the type of elements that can appear inside them. This is due to how the DOM hoists components out as invalid content. The workaround is to use the is attribute. Instead of putting the component inside those HTML tags, you can add the component to the element itself—for example, <table> <tr is="my-row"></tr></table>. The tr element will now be associated with the my-row component. We can create our own tr component to match whatever functionality we like. This limitation doesn’t apply to inline, x-templates, or .vue components. For more information on this attribute, check out the official guides at http://mng.bz/eqUY.

6.1.3. Local registration

Local registration restricts scope to only one Vue instance. We can do this by registering it with the component’s instance option. After the component is registered locally, it can only be accessed by the Vue instance that registered it.

In listing 6.2 we see a simple example of a local component. It looks similar to the global component that we registered before. The biggest difference is that we have a new instance option called components.

The components instance option declares all components needed for that Vue instance. Each component is a key-value pair. The key is always the name of the component that you’ll reference later inside the parent template. The value is the component definition. In listing 6.2 the name of the component is my-component. The value is Component. Component is a const variable that defines what’s inside the component.

In the following listing, we name our component my-component and the variable Component. You can name these whatever you like, although, as mentioned earlier, try to name the component using hyphens and lowercase, also known as kebab case. It’s good practice.

Listing 6.2. Registering a local component: chapter-06/local-component-example.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
    <my-component></my-component>
  </div>
  <script>
      const Component = {                                      1
          template: '<div>Hello From Local Component</div>'    2
      };
      new Vue({
          el: '#app',
          components: {'my-component': Component}              3
      });
  </script>
  </body>
</html>

  • 1 Shows the const variable that has the component declaration in it
  • 2 This is the template that will be displayed for this component.
  • 3 Shows the components instance option that declares components

Load the web page in a browser and you’ll see “Hello From Local Component.” If not, double-check the console for any errors. It’s easy to leave a typo in, forget to surround the template in the <div> tag, or forget to close all your HTML tags.

Kebab case vs. camelCase

Although you can name your components whatever you like, there’s one caveat. Inside your HTML templates you must use the kebab case (lowercase with hyphens) equivalent to the name you chose. Let’s say you register your component “myComponent”. When using camelCase, your HTML templates must be in kebab case. Therefore, the component would be named <my-component>. This is also true for PascalCase. If you name your component, MyComponent, then your HTML template must be in kebab case, <my-component> as well. This rule also applies to props, which we’ll learn about later.

Later, when we look at single-file components, this won’t be a problem. Until then, stick with kebab case. You can learn more about camelCase versus kebab case in the official documentation at http://mng.bz/5q9q.

6.2. Relationships in components

Imagine you’re designing a commenting system. The system should display a list of comments from each user. Each comment needs to contain the username, time and date, and the comment. Each user of our system can delete and edit each of their own comments.

Your first thought might be to use the v-for directive. This worked well in the last chapter when it was used to iterate through our inventory list. That could work, but let’s say our requirements change, and now we need to add a way to show threaded comments, or a new upvote and downvote system is needed. The code will become complicated very quickly.

Components can help solve this problem. In such a relationship, comments will be displayed in a comment-list component. The parent (the Vue.js root instance) is responsible for the rest of the app. The parent also contains a method to retrieve all the comment data from a backend. The parent passes down the data it retrieved to the child component, comment-list. The comment-list component is responsible for displaying all the comments passed down to it.

Keep in mind that each component has its own isolated scope, so it should never access the parent’s data directly. That’s why we always pass that data down. In Vue.js, the data that’s passed down is called props. This is short for properties; but inside Vue.js you must refer to it as props. Child components must explicitly state each prop it expects to receive using the props option. This option will reside in the Vue.js instance and will contain an array in the form like this: props: ['comment']. In this example, 'comment' is a property that will be passed down into the component. If we had multiple props, then we’d separate each with a comma. Props are one-way from the parent component to the child component (figure 6.2).

Figure 6.2. A parent component can send data to a child component.

In chapter 4, we discussed how the v-model directive creates a two-way data binding on form inputs and text area elements. Changes to v-model elements update the data properties in the Vue.js instance and vice versa. But components form a one-way data binding. When the parent updates properties, it flows down to the child component and not the other way around. This is an important distinction because it prevents the child from accidentally mutating the parent’s state. If you mutate state, you’ll see an error in your console, like the one in figure 6.3.

Figure 6.3. Error in console warns against mutating props directly.

Note that all values are passed by reference. If an object or array is mutated in the child, it will affect the parent state. This isn’t always the desired outcome and should be avoided. Instead you should make the changes in the parent only. Later in this chapter we’ll see how we can use events to update data from the child to the parent.

6.3. Using props to pass data

As mentioned, props are used to pass data from the parent to the child components. Props are intended for one-way communication only. You can think of props as variables the component has that can only be assigned from the parent.

Props also can be validated. We can make sure the values passed in follow a certain type of validation. We’ll look at this too.

6.3.1. Literal props

The easiest to use type of props are literal props. They’re plain strings that we can pass in to the component. Inside the template, we create the component in the usual manner, but we add our new prop within the angle brackets of our component as an additional attribute, <my-component text="World"></my-component>. This text will be passed down as a string to the text prop we created. The template will interpolate it within the curly braces.

Be aware, this is a common mistake by many beginners: oftentimes, you might want to pass in a real value into the prop, not only a string. You might accidentally pass in a string instead of passing in a value. To pass in a value, you’ll need to make sure you use the v-bind directive as we’ll see in the next section.

In listing 6.3 we see an example of passing in a literal prop. Copy this example into your editor and try it out for yourself. You’ll see that my-component has the passed-in value of "World". You can display that value using the text prop inside the template, as shown here.

Listing 6.3. Using literal props in our component: chapter 06/literal-props.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
    <my-component text="World"></my-component>     1
  </div>
  <script>
  const MyComponent= {
    template:'<div>Hello {{text}}! </div>',        2
    props:['text']
  };
  new Vue({
    el: "#app",
    components: {'my-component': MyComponent}
  });
  </script>
</html>

  • 1 Shows the component with passed-in text literal
  • 2 The template displays Hello and the passed-in prop

6.3.2. Dynamic props

Dynamic props are props that are passed in from the parent that are bound to a property that can change (unlike literal props, that are static text). We can use the v-bind directive to make sure it’s passed in correctly. Let’s update our example from the previous section and pass in the message to our new prop named text.

The component <my-component v-bind:text="message"></my-component> has a new v-bind directive attribute. This will bind the prop text to our new message. The message is a property from our data function.

If you’ve been following along in previous chapters, you may have noticed that data, in our Vue.js instance, is no longer an object data: { } as you can see in listing 6.4. This is an intentional choice. Components behave a little differently, and data must be represented as a function, not an object.

If I add a data object to my-component, an error displays in the console. To stay consistent, we’ll use data as a function for both our components and the root Vue instance for the rest of the book.

This listing is an example of using dynamic props. Copy this into an editor and try it for yourself.

Listing 6.4. Using dynamic props: chapter 06/dynamic-props.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
    <my-component v-bind:text="message"></my-component>     1
  </div>
  <script>
  const MyComponent = {
    template: '<div>Hello {{text}}! </div>',                2
    props: ['text']
  };
  new Vue({
    el: "#app",
    components: {'my-component': MyComponent},
    data() {
      return {
        message: 'From Parent Component!'                   3
      }
    }
  });
  </script>
</html>

  • 1 Uses the v-bind directive to bind message from parent to text in child
  • 2 This is the template that displays the text prop.
  • 3 The data function that returns the message

Before we move on, let’s imagine that we need to update our program to add three counters. Each counter needs to start at zero and increment independently. Each counter is represented by a button that you can click to increment. How can we do this with components?

Let’s take the code from listing 6.4 and update it. Add a data function to MyComponent and a counter. Your first thought might be to add a global variable. Let’s try that and see what happens.

As you can see from listing 6.5, we added the component three times. We created a global const object called counter, and it’s initialized to zero. In our template, we created a simple binding to the click event using the v-on directive. The counter variable will increment by one on every click.

Listing 6.5. Dynamic props with global counter: chapter 06/dynamic-props-counter.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
  <my-component></my-component>                    1
  <my-component></my-component>                    1
  <my-component></my-component>                    1
  </div>
  <script>
  const counter = {counter: 0};                    2
  const MyComponent= {
    template:'<div><button v-on:click="counter +=
 1">{{counter}}</button></div>',                 3
    data() {
      return counter;                              4
    }
  };
// ...
  </script>
</html>

  • 1 Lists component three times
  • 2 Shows the global variable counter
  • 3 The counter increments on every click.
  • 4 The data function returns the global counter.

Open your browser to the code you wrote. Click the button on the page a few times and see what occurs (figure 6.4).

Figure 6.4. Dynamic props example in a browser.

You might be surprised to see that every counter increments when we click any of the buttons. This is certainly not what we want, although it’s a good illustration on how to share scope.

Take the listing from 6.5 and update it so we can correct this problem, as shown in listing 6.6. Remove the const counter and update the data function. Instead of having the data function return the global counter, have it return its own counter. This counter is scoped locally for the component, and therefore isn’t shared with other components.

Listing 6.6. Updating counters with correct return object: chapter 06/dynamic-props-counter-correct.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
  </div>
  <script>
  const MyComponent= {
    template: '<div><button v-on:click="counter +=
 1">{{counter}}</button></div>',
    data() {
      return {
        counter: 0                1
      }
    }
  };
// ...
  </script>
</html>

  • 1 The data function returns a counter.

Fire up your browser and open the updated code you wrote. Click a few of the buttons and observe the counter (figure 6.5).

Figure 6.5. Dynamic props with a local scope counter example.

This time things look correct! As we click each button, the counter increments by itself only and doesn’t affect the other counters.

6.3.3. Prop validation

Vue.js has a nice feature called prop validation that ensures that the props that we receive from the parent pass validation. This can be particularly useful when working on a team, where multiple people use the same component.

Let’s begin by checking the type of our props. Vue.js provides the following native constructors to make this possible:

  • String
  • Number
  • Boolean
  • Function
  • Object
  • Array
  • Symbol

In listing 6.7, you can see the use of prop validation. We’ll first create a component called my-component and pass in values to it. The component will display those values in its template.

Instead of creating a prop array, prop: ['nameofProp'], we create an object. Each object is named after the prop. We then create another object to specify the type; we can add either required or default. Default refers to the default value if no value is passed in to the prop. If the type of property is an object, it must have a default value assigned. The required property, as the name suggests, requires the property to be added to the component during creation in the template.

The last thing you’ll notice from listing 6.7 is the even prop. This is called a custom validator. In this case, we’re checking whether or not the value is even. If it’s even, it’ll return true. If it’s not even, then an error will be shown in the console. Keep in mind that custom validators can perform any type of function you like. The only rule is that they must return true or false.

Keep in mind also that a single colon (:) by itself is shorthand for v-bind. This is similar to the way the at symbol (@) is shorthand for v-on.

Listing 6.7. Validating props: chapter 06/props-example.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
    <my-component :num="myNumber" :str="passedString"
           :even="myNumber" :obj="passedObject"></my-component>           1
  </div>
  <script>
  const MyComponent={
    template:'<div>Number: {{num}}<br />String: {{str}}                  2
              <br />IsEven?: {{even}}<br/>Object: {{obj.message}}</div>',
    props: {
      num: {                                                              3
        type: Number,
        required: true
      },
      str: {                                                              4
        type: String,
        default: "Hello World"
      },
      obj: {                                                              5
        type: Object,
        default: () => {
          return {message: 'Hello from object'}
        }
      },
      even: {                                                             6
        validator: (value) => {
          return (value % 2 === 0)
        }
      }
    }
  };
  new Vue({
    el: '#app',
    components:{'my-component': MyComponent},
    data() {
      return {
        passedString: 'Hello From Parent!',
        myNumber: 43,
        passedObject: {message: 'Passed Object'}
      }
    }

  });
  </script>
  </body>
</html>

  • 1 Passes in values to my-component
  • 2 The MyComponent template is used to display all the properties.
  • 3 Number validation must be present.
  • 4 String validation includes a default value.
  • 5 Object validation has a default message.
  • 6 Custom validator has to check whether or not the number is even.

Open a browser and run the code in this example. The output is shown in figure 6.6.

Figure 6.6. Validation number, string, and object using prop validation.

This is what we expect! But is there a problem? If you look at the code, you’ll notice our custom validator checks whether a number is even or odd. If it’s odd, it returns false. Why don’t we see false where it shows IsEven?

In fact, Vue.js does show it as false! But it’s not in the template. By default, prop validations don’t prevent passed-in values from showing up in the template. Vue.js checks the validation and emits warnings in the console. Open the Chrome console and look. Your console should look similar to figure 6.7.

Figure 6.7. Error showing validation failure.

The error shows that our custom validator failed for prop even. This is good to know, and we should change the passed in number to an even number. Keep this type of error in mind while using prop validations.

6.4. Defining a template component

Until now, we’ve used local and global registration to define our components. Templates in each of our components have been defined as a string. This can be problematic as our components get larger and more complicated. The template strings aren’t the easiest to work with due to different development environments that may cause issues with syntax highlighting. In addition, multiline template strings need escape characters, which clutter our component definition.

Vue.js has multiple ways to display templates that can help fix this. We’ll discuss this and how we can use ES2015 literals to make our component templates easier to use.

6.4.1. Using inline template strings

One of the simplest ways of working with a template is to use it inline. To make this work, we need to include the template information inside the component when it’s added to the parent template.

In listing 6.8, you can see that we declare the component as <my-component :my-info="message" inline-template> in the template. The inline-template tells Vue to render the contents of the component within the opening and closing of the my-component tag.

One downside of using inline templates is that they separate the template from the rest of the definition of the component. For smaller applications, this will work, although it’s not recommended for use in larger applications. In larger applications, you should look into the single-file components that we’ll discuss in the next chapter.

Listing 6.8. Using templates inline: chapter 06/inline-component-example.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
  <div id="app">
    <my-component :my-info="message" inline-template>         1
      <div>
          <p>
            inline-template - {{myInfo}}                      2
          </p>
      </div>
    </my-component>
  </div>
  <script>
  const MyComponent = {
    props: ['myInfo']
  };

  new Vue({
      el: '#app',
      components: {'my-component': MyComponent},
      data() {
        return {
          message: 'Hello World'
        }
      }
  });
  </script>
</body>
</html>

  • 1 The inline template displays HTML.
  • 2 Shows the passed-in property

6.4.2. Text/x-template script elements

Another way of defining a template in our component is by using text/x-template script elements. In this case, we create a script tag with the type text/x-template.

In listing 6.9, we use the text/x-template to define the template for my-component. The thing to remember here is that you must define the script as type="text/x-template", otherwise it won’t work.

Once again, we run into the same downside as with the inline templates. The biggest problem is that we’re separating the component definition from the template. This will work, but it’s useful only in smaller applications and not in larger ones.

Listing 6.9. Working with text/x-templates: chapter 06/x-template-example.html
  <!DOCTYPE html>
  <html>
  <head>
  <script src="https://unpkg.com/vue"></script>
  </head>
  <body>
  <div id="app">
    <my-component></my-component>
  </div>
  <script type="text/x-template" id="my-component">        1
    <p>
      Hello from x-template
    </p>
  </script>
  <script>
  const MyComponent = {
    template: '#my-component'
  };
  new Vue({
    el: '#app',
    components: {'my-component': MyComponent}
  });
  </script>
  </body>
</html>

  • 1 Shows the x-template script

6.4.3. Using single-file components

Earlier in our examples, we used strings to represent our templates in our components. With ES2015 template literals, we can help eliminate several problems we had using strings. In ES2015, if you surround a string with a backtick (`), it becomes a template literal. Template literals can be multiline without having to escape them. They can have embedded expressions in them, too. This makes writing our templates much easier.

With all that said, ES2015 template literals still have several of the same drawbacks as strings. It still looks a little cluttered in your component definition, and certain development environments won’t have syntax highlighting. You have one more option to help fix all these problems: single-file components.

Single-file components combine your template and definitions into one .vue file. Each one has its own scope, and you don’t have to worry about forcing unique names for every component. CSS is also scoped for each component, which is helpful in those larger applications. To top it all off, you no longer have to worry about dealing with string templates or having to work with unusual script tags.

In the following listing, you can see that the HTML is surrounded by the template tag, unlike in our previous examples. The .vue file uses ES2015 export to return the data for the component.

Listing 6.10. Single-file components: chapter 06/single-file-component.vue
<template>                             1
  <div class="hello">
    {{msg}}
  </div>
</template>

<script>
  export default {                     2
  name: 'hello',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

  • 1 The template displays information for the component.
  • 2 Shows the ES2015 export of the data

To use single-file components, you’ll have to get familiar with several modern build tools. You’ll need to use a tool such as Webpack or Browserify to build the .vue code. Vue.js has made this process easy with its own scaffolding generator called Vue-CLI. It contains all the necessary build tools. We’ll discuss tools in the next chapter. For now, know that there are many ways to work with templates and that single-file components are the way to go for larger applications.

6.5. Working with custom events

Vue.js has its own event interface. Unlike normal events that we saw in chapter 3, custom events are used when passing events from parent to child components. The event interface can listen to events using the $on(eventname) and trigger events using $emit(eventName). Typically, $on(eventname) is used when sending events between different components that aren’t parent and child. For parent and child events, we must use the v-on directive. We can use this interface so parent components can listen directly to child components.

6.5.1. Listening to events

Imagine you’re creating a counter. You want to have a button on the screen that increments the counter by 1 every time it’s clicked but you’d like to have that button in a child component and the counter in a parent Vue.js instance. You don’t want the counter to mutate inside the child component. Instead, it should notify the parent that the counter should be updated. On every button click, the counter in the parent needs to update. Let’s look at how to do this.

Let’s start by creating a component. When we add it to our template, we’ll need to use the v-on directive and create a custom event. As you can see in listing 6.11, we’ve added the component and created a custom event called increment-me. This custom event is bound to the incrementCounter method that we defined in the parent Vue instance. We’ll also add a normal button that’s bound to the click event that triggers incrementCounter, too. This button resides in the parent’s template.

Inside the definition of our component, we’ll need to add a button. We’ll again use the v-on directive bound to the click event. This triggers the childIncrementCounter method that we defined in the child component.

The childIncrementCounter has only one responsibility, and that’s to emit the custom event we created earlier. This is where it might get confusing. We’ll use this.$emit('increment-me') to trigger the bound event, incrementCounter, defined in the parent methods. We’re triggering the parent Vue.js instance incrementCounter method, which will increment the counter. This is powerful and allows us to modify values in the parent while keeping the one-way data principle intact.

Listing 6.11. Incrementing a parent counter using $emit: chapter 06/event-listen.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
  <body>
  <div id="app">
    {{counter}}<br/>
    <button v-on:click="incrementCounter">Increment Counter</button>     1
    <my-component v-on:increment-me="incrementCounter"></my-component>   2
  </div>
  <script>
  const MyComponent = {
    template: `<div>
      <button v-on:click="childIncrementCounter"                         3
        >Increment From Child</button>
    </div>`,
    methods: {
      childIncrementCounter() {
        this.$emit('increment-me');                                      4
      }
    }
  };
  new Vue({
    el: '#app',
    data() {
        return {
            counter: 0
        }
    },
    methods: {
      incrementCounter() {
        this.counter++;                                                  5
      }
    },
    components: {'my-component': MyComponent}
  });
  </script>
  </body>
</html>

  • 1 Shows the button to increment the counter from the parent
  • 2 Indicates the component that sets the increment-me event to incrementCounter
  • 3 Indicates the component button that triggers the childIncrementCounter method
  • 4 Emits the increment-me event
  • 5 Shows the method to increment the counter by 1

If we load the Chrome browser, you’ll see two buttons. Both will increment the counter setup in the parent and be displayed in the component (figure 6.8).

Figure 6.8. Shows two buttons; both increment the counter from the parent.

6.5.2. Modifying child props using .sync

In most situations, we don’t want to have the child component mutate a prop from the parent. We’d rather have the parent do it. This is one of the fundamental rules of the one-way data flow that we mentioned earlier in the chapter. Vue.js allows us to break this rule, though.

The .sync modifier allows us to modify values in a parent component from inside a child component. It was introduced in Vue 1.x and removed from Vue 2.0 but the Vue.js core team decided to reintroduce it in 2.3.0+. With all that said, be cautious when using it.

Let’s create an example that shows how .sync can update a value. Modify the code in listing 6.11 by updating my-component and childIncrementCounter. To begin, we’ll look at the .sync modifier. To use the .sync modifier you can attach it to any prop on the component. In listing 6.12, it’s attached at <my-component :my-counter.sync="counter">. The my-counter prop is bound to counter.

The .sync modifier is syntactic sugar for this <my-component :my-counter="counter" @update:my-counter="val => bar = val"></my-component>. The new event created is called update. That event will take the my-counter prop and assign it to whatever variable is passed in.

To make this work, we still need to emit our new event that was created and pass in the value we want the counter to be updated to. We’ll use this.$emit to do this. The this.myCounter+1 is the first argument that will be passed to the update event.

Listing 6.12. Working with .sync to modify props from child chapter 06/event-listen-sync.html
...
<my-component :my-counter.sync="counter"></my-component>         1
   ...
  const MyComponent = {
    template: `<div>
      <button v-on:click="childIncrementCounter"
>Increment From Child</button>
    </div>`,
    methods: {
      childIncrementCounter() {
        this.$emit('update:myCounter', this.myCounter+1);        2
      }
    },
    props:['my-counter']

  • 1 Shows the component setup with the .sync modifier
  • 2 Emits to update event followed by a comma

If we load the browser, you’ll see two buttons. If we click either one, it updates the counter (figure 6.9).

Figure 6.9. This example uses .sync to modify the counter.

Exercise

Use your knowledge from this chapter to answer these questions:

  • How do you pass information from a parent to a child component? What do you use to pass information from a child component back to a parent component?

See the solution in appendix B.

Summary

  • Local registration for components has local scope. It can be created by using the components option when constructing a new Vue instance.
  • Global registration for components uses the Vue.components instance operator where the component is defined.
  • Components use one-way data binding between the parent and child component.
  • Props are used in components to define what can be passed to them.
  • Single-file components combine all template and script information into one file.
  • You can use $emit to send information to the parent component.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset