Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

The Formidable v-model

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Ya know what just occurred to me? We haven't talked at all about form elements yet! And what a wonderful coincidence! Because our next challenge is to add a search bar input to filter the product list.

The search bar will, of course, contain some HTML. It will also need to manage the value of the search bar and help us know when we should filter the product list. That's enough that I think we should isolate this in a new component.

Creating the search-bar Component

In components/, create a new file called search-bar.vue. Add the <template> with <div> and an <input> with class="form-control", a placeholder and type="search".

<template>
<div>
<input
class="form-control"
placeholder="Search products..."
type="search"
>
</div>
</template>
... lines 10 - 16

So nothing special. If you're wondering why I added the div, it's just because we're going to have more than just the input later.

At the bottom add the <script> section with the basic export default and name: 'SearchBar'.

... lines 1 - 10
<script>
export default {
name: 'SearchBar',
};
</script>

Love it! Over in catalog.vue, let's see... change the div around the title to col-3, and then, below, add a new <div class="col-9">. Inside, we haven't imported the search-bar component yet... but ah! Let's try to use it anyway! Type <sea and hit tab to auto-complete that.

<template>
... line 2
<div class="row">
... lines 4 - 9
<div class="col-9">
<search-bar />
</div>
</div>
... lines 14 - 23
</template>
... lines 25 - 76

When we did that, because PhpStorm is awesome, it added the import and put this down in the components section. PhpStorm, did we just become best friends?

... lines 1 - 25
<script>
... lines 27 - 29
import SearchBar from '@/components/search-bar';
... lines 31 - 74
</script>

Let's check the browser. Boom! That's a sweet search bar. Now let's bring it to life

Binding a data to the Input

To filter the product list, we need to know the value of the input. There are a few ways to get this, but the simplest is to add a piece of data to the component that we keep "in sync" with the text of the input. Add data and return an object with one item: searchTerm set to an empty string to start.

... lines 1 - 12
export default {
... line 14
data() {
return {
searchTerm: '',
};
},
};
... lines 21 - 22

To "bind" this data to the input, we need to do two things. First, set the input value to the data: :value="searchTerm".

<template>
<div>
<input
:value="searchTerm"
... lines 5 - 7
>
</div>
</template>
... lines 11 - 22

Now with just that, if move over to the browser and look at the Vue dev tools, we can click on SearchBar, change the searchTerm data and... voilà! The input text updates... which shouldn't be too surprising. That's Vue goodness in action.

But what we can't do yet is type in the box and have it update that data. That's the second part of binding an input to data.

@input for when the Input Changes

How can we do that? It's lovely: by listening to an event on the input.

Remember: the way we listen to an event is with v-on: and then the name of the event, like click or keydown. Well, in practice, we use the shortcut @ syntax, so @click or @keydown.

In this case, use @input. The input event is a native, normal JavaScript event that's similar to keyup: it will trigger any time the input's value changes.

Inside the quotes, so far, we've set this to something like someMethod. Then, we've added a methods option, with a someMethod function, and put whatever code we want to run right there.

That's totally valid, but if what you need to do is simple, you can also write an expression right inside the attribute. In this case, when the input changes, we only need to set the searchTerm data to the new string.

Inline v-on Expression

To do that, say searchTerm - which we know really means this.searchTerm - equals $event.target.value.

... lines 1 - 2
<input
... lines 4 - 7
@input="searchTerm = $event.target.value"
>
... lines 10 - 23

That... deserves an explanation. If we set this to a method name, then the method will receive an event argument. Then we can say event.target.value to get this input's value.

When you write an inline expression, Vue magically makes the event object available as a $event variable.

But... hmmm... ESLint is mad: it doesn't like how I ordered my attributes. From a technical standpoint, we can put these in whatever order we want. But as a standard, ESLint likes to have listeners - like @input - on the bottom.

Ok, let's take our input for a test drive! Back at the browser, find SearchBar on the dev tools. Now, as we type... yea! The data is updating!

What we effectively just did is took a piece of data - searchTerm - and bound it to a form input. When the data changes, the input changes. When the input changes, the data changes.

Hello v-model!

This is a very common thing to do in Vue. In fact, it's so common that Vue created a special directive just for it.

Check this out: we can delete the @input and :value lines and replace them with one line that will do the exact same thing. It's v-model="searchTerm".

... lines 1 - 2
<input
v-model="searchTerm"
... lines 5 - 7
>
... lines 9 - 22

This is one of the last important directives that we haven't already talked about. v-model="searchTerm" literally means: set the value attribute to searchTerm and, on input, update the searchTerm data with the input value. It's identical to what we had before.

Now v-model does act a little bit different with things like checkboxes or select elements, but those are minor normalizations to make v-model work how you want it to work for those elements. Back at the browser, everything is hooked up just like before.

You're probably going to see v-model all the time. For me, it helps to remember what it's really doing behind the scenes: setting the input's value to the searchTerm data and, on change, updating that data for you.

Next: let's use this search term to filter our product list as the user is typing!

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.0
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.1.0
        "eslint-plugin-import": "^2.19.1", // 2.20.2
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.5
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.11
        "vue-loader": "^15.9.1", // 15.9.2
        "vue-template-compiler": "^2.6.11", // 2.6.11
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}
userVoice