I'm making a course on Laravel Sanctum: MasteringAuth.com

Create reusable form components using Vuejs 3 multiple v-models

Constantin Druc ยท 07 Jan, 2022

[00:00] So v-model is a directive we use to achieve two-way data binding; we typically use it with form fields. We add a v-model, pass it a reactive value, and we're done. Any time this value is updated somewhere else, the form input value gets updated as well and vice versa. But what most people don't know is that we can also use v-model on vue components.

[00:31] However, until now we could only have one v-model per component. Not anymore. With Vue 3 we can have as many v-model directives as we need, and that can be pretty convenient as you will see in this video.

[00:47] Let's take a checkout form as an example. To keep things simple, this form only has a couple of fields. We have the first name, last name, email, and then a bunch of fields related to the delivery address. We have street, number, postcode, and city.

[01:08] As you may know, most checkout forms allow you to specify a delivery address, and then a billing address. However, this means we'd have to duplicate all these fields to create a new section for the billing address. In this situation when we have a bunch of related fields like we have here; all these fields are part of an address, it might make sense to create a new vue component. So let's do that.

[01:36] I'll go here, and say AddressFieldGroup. Now, as I said before, previously you could only have one v-model per component. But in our case we need more than one; we need one for the street, one for the number, postcode, and city. Luckily, this is now possible in Vue3.

[01:59] However, it doesn't work by adding v-model alone; you need to pass it an argument, and the way you do that, is by typing colon, and then the name of the argument. For example, street. Let's set this to form.deliveryAddress.street, and continue with the others. We'll have street number, we'll then have postcode, and finally we have city.

[02:31] Now let's create this new AddressFieldGroup component. I'll add a template tag, and then go and copy the whole section.

[02:50] If we look at the fields, we used to get our values from the form delivery address object. Not anymore. Now we receive the values as props. But to use them we need to define them under the props key. So we'll go here, and say script, export default, and then props. And we'll have street, which will be of type string, and let's set a default to an empty string. And let's add the others as well. We'll have street, number, postcode, and city.

[03:33] Now that we've defined the props, we can use them to set the value for every field. So let's do that. I'll go here, and instead of v-model, we'll have value, street, for the street number, we'll have value, street number, for postcode, we'll have postcode, and for city, we'll have city. Let's see what we have so far. I'll go to the checkout form component and import the AddressFieldGroup component, register it, and let's change some of the default values, just so we see them in the browser.

[04:26] Let's go in the browser, and here they are. However, if we open the vue dev tools, and select the delivery address, change the street for example, we'll see that the value from the field doesn't get passed up to the checkout form component, we still have the old value.

[04:46] To communicate that change we need to send an update event from our AddressFieldGroup component. So let's do that. We'll go here, find the street, and do input; so on input, we'll emit an update event, but we want to do so for a specific v-model. To specify the v-model, we can do colon and then the name of the v-model, in our case, street, then to get the value of the input, we can do $event.target.value. Let's do the same for all the other fields. We'll have street, number, postcode, and finally, city.

[05:37] Let's go in the browser and see if this worked. I'll refresh, select the delivery address, change the street, the number, postcode, and city. And of course, it worked. All the values have been updated. One more thing I want to do, is pass a label prop to allow the user to specify the section title. So we'll have label here, we'll accept it as a prop, and then here, on the checkout form, we'll do label equals "Delivery address", and here it is. Now, for the billing address, all we need to do is copy this and replace delivery address with billing address. Go into browser and here they are.

[06:53] Remember, when you have a bunch of fields that always go together, extract a new vue component and use multiple v-models instead. It makes everything cleaner.