Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

v-on & methods: User Interaction

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

We've talked a lot about data, props and using those in a template. But what we haven't done yet is add any behavior. So here's our next big goal: add a link to the bottom of the sidebar that, when the user clicks it, will collapse or expand the sidebar.

Adding the collapsed Data

Cool! But... where should we start? Let's think about it: in the sidebar template, we're going to need to know whether or not we are currently collapsed or expanded... because we will probably need to render different classes or styles to make that happen. And since this "is collapsed" information is something that will change while our Vue app is running, it needs to live in data.

Ok! Inside data(), add a new property called, how about, collapsed. Set it false so that the component starts not collapsed.

... lines 1 - 33
<script>
export default {
... line 36
data() {
return {
collapsed: false,
... lines 40 - 49
};
},
};
... lines 53 - 68

Back on the browser, it doesn't do anything yet, but we can see the new data.

Let's update the template to use this. Add a style attribute. Basically, if collapsed is true, we'll set a really small width. Of course we can't just start referencing the collapsed variable right now because we need to add the : in front of :style to make it dynamic.

To set the width, we could put width: in quotes then end quote, plus, and some dynamic logic that uses the collapsed variable. But... yuck! Because the style attribute can get complex, instead of creating a long string of styles, Vue allows us to set this to an object with a key for each style, like width: '500px' and margin: '10px'. In our case, I'll use the ternary syntax: if collapsed is true, set the width to 70px, else use auto.

<template>
<div
... line 3
:style="{ width: collapsed ? '70px' : 'auto' }"
>
... lines 6 - 31
</template>
... lines 33 - 68

Ok! We have a collapsed data and we're using it in the template. Testing time! In the Vue dev tools, click on Sidebar, change collapsed to true and... oh that looks awful! We'll clean that up soon. But it is working: the width dynamically changes.

Adding the Button

Of course, we're not going to expect users to change the collapsed data via their Vue dev tools. Nope: they'll need a button that will change this state.

Back on the template, let's see... after the <ul>, I'll add an <hr>, a div with some positioning classes and a button with class="btn btn-secondary".

... lines 1 - 33
<div class="d-flex justify-content-end">
<button
class="btn btn-secondary btn-sm"
>
... line 38
</button>
</div>
... lines 41 - 78

Nothing interesting yet! For the button text, if we're currently collapsed, use >>, else, use << Collapse.

... lines 1 - 34
<button
class="btn btn-secondary btn-sm"
>
{{ collapsed ? '>>' : '<< Collapse' }}
</button>
... lines 40 - 78

Oh ESLint is mad! Hmm, it thinks that the < sign is me trying to open an HTML tag! I could escape this and use &lt; - that's probably a great solution! But as you'll see, Vue will escape this for us.

Back on the browser, I'll inspect the new button, and right click to "Edit as HTML". Yep! Vue automatically escapes any text that you render. That < becomes &lt; automatically.

Hello v-text

Oh, and, by the way, when you have an element and the only thing inside of it is some dynamic text - like our button - there is one other way to render that text. Copy our dynamic code, add a new attribute called v-text, set it to our dynamic code... and delete the curly braces.

Now Vue is happy. v-text is the third Vue directive that we've seen, after v-bind and v-for. It's not a particularly important one... it's just an alternative way to set the "text" of an element and... it's a tiny bit faster. I mostly just wanted you to see it.

... lines 1 - 34
<button
... line 36
v-text="collapsed ? '>>' : '<< Collapse'"
/>
... lines 39 - 77

Adding a new Method

Let's get back to the real goal: when the user clicks this button, we need to do something! Specifically, we need to change the collapsed data.

We know that the variables that we have access to in the template are the keys from data and props... though we don't have any props in this component. It turns out that there are a couple other ways to add stuff to your template. One allows you to add functions.

Check it out: it doesn't matter where... but I'll do it after data(), add a methods: option set to an object. Create a function called toggleCollapsed() and, to start, just say console.log('CLICKED!').

... lines 1 - 43
<script>
export default {
... lines 46 - 61
methods: {
toggleCollapsed() {
console.log('CLICKED!');
},
},
};
</script>
... lines 69 - 83

The idea is that, "on click" of this button, we will tell Vue to call this method.

OnClick with v-on

How... do we do that? By using our fourth directive - and a very, very important one. It's called v-on. Inside the button element, add v-on: and then the name of the normal DOM event that you want to listen to, so: click. Set this to the name of the method that should be called on click: toggleCollapsed.

<template>
... lines 2 - 33
<div class="d-flex justify-content-end">
<button
... line 36
v-on:click="toggleCollapsed"
... line 38
/>
</div>
... line 41
</template>
... lines 43 - 83

Yep, the toggleCollapsed function is now available in the template because we added it to the methods options. PhpStorm even lets you Ctrl or Cmd click to jump to it!

So... let's try it! Back on your browser, click and... then scroll down. These errors up here were from when we were in the middle of working. And... yes! There's our log!

The v-on Shortcut

v-on is one of the most important directives and we're going to use it all the time. Scroll up a little to the :href attribute. We know that this is really v-bind in disguise: v-bind is such a common directive, that Vue gives us this special shortcut.

Vue also has a shortcut for v-on: the @ symbol. Want to run some code "on click", use @click or @mouseover or @ whatever event you want.

... lines 1 - 34
<button
... line 36
@click="toggleCollapsed"
... line 38
/>
... lines 40 - 83

Updating the State

Ok: we have a button and when we click it, it calls the toggleCollapsed method. The final step is to change the collapsed state so that the template updates. How? this.collapsed = !this.collapsed.

... lines 1 - 44
export default {
... lines 46 - 61
methods: {
toggleCollapsed() {
this.collapsed = !this.collapsed;
},
},
};
</script>
... lines 69 - 83

If you were eagerly waiting for some complex & impressive code to change the data, I'm sorry to disappoint you.

The secret to this working is that any data keys are accessible as a property on this. In the next chapter, we'll look deeper into how that works. But for now, it feels good: we change the value of this.collapsed and... apparently, Vue will re-render.

Sound too good to be true? Let's try it! Click the button. It works! Over on the Vue dev tools, on the Sidebar component, we can watch the collapsed state change.

And... congratulations! We've now talked about the core parts of Vue. Seriously! To get this working, we added a collapsed data, used that in the template, and, on click, called a toggleCollapsed method that we created... which changed the collapsed data. Vue was then smart enough to re-render the template.

We'll see this basic flow over and over again.

But before we do, I want to talk more about how the this variable works in Vue. It's at the heart of Vue's magic. And if we understand it, we'll be just a little bit closer to being unstoppable. That's next.

Leave a comment!

5
Login or Register to join the conversation
shirleenkneppers Avatar
shirleenkneppers Avatar shirleenkneppers | posted 2 years ago | edited

Hi Ryan, I am getting this error. Do you have anyway to remedy this?

<br />Script cache:clear returned with error code 255<br />!! <br />!! In ContainerBuilder.php line 1089:<br />!! <br />!! Class "Doctrine\Common\Cache\ArrayCache" does not exist <br />!! <br />!! <br />!! <br />Script @auto-scripts was called via post-update-cmd<br />

Reply
shirleenkneppers Avatar
shirleenkneppers Avatar shirleenkneppers | shirleenkneppers | posted 2 years ago | edited

Ok so apparently doctrine cache has been deprecated. https://github.com/doctrine/cache/issues/354, So tried installing an older cache library to make it work. You will need to install this version of Doctrine cache using this in your terminal.

`

     composer require doctrine/cache:1.12.1

`

You need to do this before installing the project or you will get the error. I hope this helps someone.

Reply

Hey shirleenkneppers

That's weird. How did you get newer cache package? I checked code we do have this package and it's locked on version 1.10.2. I pretend you tried to upgrade project to latest version which we don't recommend to do with downloaded code 'cause you can get issues

Cheers!

Reply
shirleenkneppers Avatar
shirleenkneppers Avatar shirleenkneppers | sadikoff | posted 1 year ago

Hi @Vladimir

I normally don't update the files. I started getting errors on Yarn watch at chapter 16 and for some reason it just didn't wanted to show any of the changes on the webpage. I tried fixing the errors but as you can imagine that some of these fixes might have required me to do updates on packages. So after a day of struggling to just be able to use the files I found this and it is working fine. I wouldn't have updated if it wasn't needed. I don't remember the errors but they where hindering my progress.

Regards

Reply

Anyways good debugging. I'm curious what is the real issue which forced you to update packages, we will try to investigate it... I hope it is not something serious.

Cheers!

Reply
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