Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Adding a Watcher

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

Let's add a watcher! Create a new option called watch: {} set to an object of property names. For instance, since we want to run code when currentCategoryId changes, we'll add currentCategoryId set to the function that will be called. When Vue executes this, it will pass us two arguments: the new value of currentCategoryId and the old value. For now, just console.log(newVal, oldVal).

... lines 1 - 25
<script>
... lines 27 - 32
export default {
... lines 34 - 57
watch: {
currentCategoryId(newVal, oldVal) {
console.log(newVal, oldVal);
},
},
... lines 63 - 92
};
</script>

Let's see if this works! Move over to the browser: the console looks clear. Since we haven't actually added a way to change the currentCategoryId from within our app, head over to the Vue Dev Tools and change it there. I'll set it to 24, go back to the console and... yes! You can see the new value first and then the old value.

Load Products When currentCategoryId Changes

Back to catalog.vue! I'm actually going to delete these two arguments because I don't need them. Instead, our code can reference the new value directly via the currentCategoryId prop. Ok: when the currentCategoryId changes, we want to call this.loadProducts(). For the search term pass null for now.

... lines 1 - 57
watch: {
currentCategoryId() {
this.loadProducts(null);
},
},
... lines 63 - 95

This will trigger loadProducts()... which in turn will read the new currentCategoryId, get our products back from the server and update the products data. It's a fool-proof plan!

So let's check it out! I'll go back to the Vue Dev Tools, change the data to 23 and... ah! It works! With the loading screen and everything! I love that we can even change it to null and it goes to "All products". Awesome!

The only rough part is that if I, for example, search for "disk"... Actually let's try this under "office supplies". Search for "disk"... Then go find the <Products> component in the Vue Dev Tools and change the currentCategoryId to... let's say 24.

Ah! It returns everything! It did not apply the searchTerm on the products. The reason, of course, is that, in our watcher, we're passing this.loadProducts(null). Yep, we're saying:

Hey Vue! Load the products with no searchTerm".

And darn it, Vue is following our directions perfectly!

Suddenly we Do Want a searchTerm Data

If you look at data, we do not have searchTerm as a data key. Why? Because, until now, we didn't need it! All we needed to do - when onSearchProducts() was called - was use the term to immediately load the products. There was no need to store the search term anywhere for later. But now we do have a need! We do need to store the searchTerm so that when the currentCategoryId changes, we know what the searchTerm is.

Ok! Re-add searchTerm to data. And, down in onSearchProducts, say this.searchTerm = term. We can now remove the term argument from loadProducts and just say this.searchTerm instead.

... lines 1 - 32
export default {
... lines 34 - 50
data() {
return {
... lines 53 - 55
searchTerm: null,
};
},
... lines 59 - 66
methods: {
... lines 68 - 72
onSearchProducts({ term }) {
this.searchTerm = term;
this.loadProducts();
},
async loadProducts() {
... lines 79 - 81
try {
response = await fetchProducts(this.currentCategoryId, this.searchTerm);
... lines 84 - 85
} catch (e) {
... lines 87 - 89
}
... lines 91 - 92
},
},
};
... lines 96 - 97

That looks good! Now... let's see: up in created(), we don't need any arguments... and same in the currentCategoryId() watcher.

... lines 1 - 32
export default {
... lines 34 - 58
watch: {
currentCategoryId() {
this.loadProducts();
},
},
created() {
this.loadProducts();
},
... lines 67 - 94
};
... lines 96 - 97

Each will now automatically use the current search term. Let's try it!

Try it one more time!

I'll click on "Office Supplies" to get a full page refresh. Search for "disk"... and go over to the Vue Dev Tools one more time. Click on <Products>. change the currentCategoryId to 24 and... wow! No products found! If I change this back to null for "all categories"... it does find the product!

Of course, it's not updating the URL when we change the category... which it probably should. For that, we would need a Vue Router: a key component of single page apps - or even "mini" single-page "sections" that you might create on part of your site. With the Router, we could click on these links and have the URL change without a full page refresh. We'll save that topic for another time.

People! Friends! You made it through our first, gigantic Vue tutorial! Congrats! You deserve a snack... and probably some outside time.

There are more things to talk about - we'll save those for a future tutorial - but wow! You are already incredibly dangerous. I hope you enjoyed this process as much as I have! Vue is a very powerful, very fun tool to work with. So go build something awesome... then tell us about it!

And, as always, if you have any questions, comments or kitten videos, let us know down in the comments. All right, friends, seeya next time!

Leave a comment!

19
Login or Register to join the conversation
Helmi Avatar

Very nice course, it will be awesome if you add a final video explaining Composition API xD

Reply

Hey Helmi,

I'm glad to hear you liked our tutorial, but unfortunately, we didn't cover that topic but thanks to your feedback we may cover it in a further tutorial

Cheers!

Reply
Alexandre C. Avatar
Alexandre C. Avatar Alexandre C. | posted 1 year ago

Hi team !

Thank you for the tutorial !

I would like to use Vue with a Symfony app. Just to be sure, is there a way to manage forms with Vue ?

I think it's not possible, but if I need to tweak a form when some operation is done, what's the best way to do it ? Flat Javascript ?

Have a good day,

Alexandre

Reply

Hey Alexandre!

> Thank you for the tutorial !

Glad you've enjoyed !

> I would like to use Vue with a Symfony app. Just to be sure, is there a way to manage forms with Vue ?

Yes... there is... and we'll talk about it at the end of our next tutorial - https://symfonycasts.com/sc... - but it depends on exactly what you want to do. You mentioned:

> but if I need to tweak a form when some operation is dne

Can you give me a specific example of what you want to accomplish? For example, do you want to, have a form, and change that form in some way after one of the other fields is input? Or something different?

Let me know!

Cheers!

Reply
Alexandre C. Avatar
Alexandre C. Avatar Alexandre C. | weaverryan | posted 1 year ago

Thank you for this fast answer ! You're amazing!

> Can you give me a specific example of what you want to accomplish? For example, do you want to, have a form, and change that form in some way after one of the other fields is input? Or something different?

Like hiding / showing part of the form depending of the user input (through AJAX & Symfony form events). Add elements to a CollectionType. Managing input type file.

I don't have a specific use-case in mind, I just started a project with Symfony & Vue, so there's not problem yet ! Haha

> Yes... there is... and we'll talk about it at the end of our next tutorial - https://symfonycasts.com/sc...
I will start it is ASAP ! :)

Have a good day,

Alexandre

Reply

Hey @Alexandre!

> Thank you for this fast answer ! You're amazing

My pleasure... but I haven't helped yet 😉

> Like hiding / showing part of the form depending of the user input (through AJAX & Symfony form events). Add elements to a CollectionType. Managing input type file

You really have two different choices from the very beginning:

1) Build the form in Symfony. You get all the nice features of Symfony's form system, including the nice rendering, validation errors, etc.

2) Build the form in HTML / Vue without Symfony's form system. In this case, you wouldn't really "submit" the form... you would collect the data from the fields and send them as JSON to some API endpoint. The advantage of this is that you have full Vue control to do whatever crazy things you want on the frontend. The disadvantage is that you're responsible for more of the form stuff: like collecting the validation errors from the server and rendering them near each field, etc.

For what you're talking about, personally, I would go with option (1). These small things - hiding/showing field, etc - are not so crazy-dynamic that I think Vue would help enough to overcome the advantages of the form system. What we're currently using - and what was just announced with first-class support in Symfony at SymfonyWorld - is Stimulus.js - https://stimulusjs.org/ - a tool that I will have a screencast on soon. Btw, if you didn't "attend" SymfonyWorld, don't worry - just follow the Stimulus docs and get it set up - you don't need any Symfony integration.

Anyways, what Stimulus allows you to do is create a Stimulus controller that "binds" to an element (I would bind it to your form element). Then you can very easily listen to events and do things, like hiding/showing fields, or handling the form collection logic in a nice way. And if it makes you more comfortable, you can absolutely use jQuery inside a Stimulus controller if you want, but most things you need to do (like finding elements, attaching listeners, adding/removing classes) is really easy in Stimulus or can be done really easily with Vanilla JavaScript.

This is a long way of saying that I really, really like Vue for big dynamic, interactive parts of my site - for example the coding challenge page on our site (built in React, technically) https://symfonycasts.com/sc... - but for rest of JavaScript - any places where you need to sprinkle/add some JavaScript to your page, Stimulus is my favorite. And you can absolutely use both - each for different problems (it's what we do on SymfonyCasts).

Cheers!

Reply
Mickaël M. Avatar
Mickaël M. Avatar Mickaël M. | posted 1 year ago

Great course, I really appreciate it. Can't wait for the next vue lesson. Can I be spoil :) Is there vue router ?

Reply
Mickaël M. Avatar

Actually I've got a question, is there an other way to pass twig's variable in Vue without Apiplatform or window (JS)? For example if I want to pass my User object from twig to vue, what the best way to do it ?

Reply

Hi Micka!

If I understand correctly, the only way to pass data from Twig to Vue is through the use of the window object, since Twig can only print things to the template. If what you mean is, how to pass data from the server to Vue, the other obvious option is to just make an HTTP request to the server asking for that information. This is common practice but it does take some time for the data to load, so in this tutorial we've chosen the much faster template way.

Reply
Mickaël M. Avatar
Mickaël M. Avatar Mickaël M. | shadowc | posted 1 year ago

Hi Matias,
That's what I meant, thanks for the answer 😉

Reply

Excellent course, have learn a lot. But at the end there is no certificate.
Can't wait for the next tutorial.

Reply

Hey Wilco

I'm happy to know that you find our tutorial useful :)
About your certificate not showing up. Can you double-check that all the chapters are marked with a check-arrow? If you skipped or missed a chapter, then you won't get the certificate until fully finishing with the course

Cheers!

Reply
Jean-tilapin Avatar
Jean-tilapin Avatar Jean-tilapin | posted 2 years ago

Excellent work, as always. Thanks, SymfonyCast. Do you know when the second part will be available ? I'm starting a new project, using Vue (and Symfony of course), I'd really like to learn some new useful tricks, especially about the vue router... :)

------
About Symfony : could you please create an up-to-date cheatsheet about Symfony with the main commands lines and recipes ?

- download symfony, start server, update composer, etc.

- the main dev tools (maker, debug toolbar, etc.) and commands

- useful recipes (doctrine, api, encore, bootstrap, yarn, etc.) with "--dev" or not.

- the proper order to install all that (if there is one)

That could be very useful :)

Reply

Hey Xav,

Thank you for the feedback! :)

Yeah, we're going to release it shortly... it should definitely happen this year, most probably after API Platform v3 tutorial. My wild guess is that it may be start releasing next month, but that's a really rough estimation from me for now. Sorry, can't say more precise

Thank you for your interest in SymfonyCasts tutorials and your patience!

About the cheatsheet - I'm not sure, I think there are some good cheatsheets over the internet made by others, so you probably could take a look at them. We're working on tutorials mostly :)

Cheers!

Reply
Fernando A. Avatar
Fernando A. Avatar Fernando A. | posted 2 years ago

Wonderful work, I like to watch these before I do the coding... now because I already know some VueFoo I will not code anything, but I managed to learn some new things anyway.

Gotta love to update knowledge from time to time :)

Reply

Thanks for the words, Fernando! I'm glad it was useful to you! :)

Reply
Peter K. Avatar
Peter K. Avatar Peter K. | posted 2 years ago

Thank you Ryan and rest of the team for this course.

It was a lot of new information for me, pretty much everything was new.

Hope your new courses will use more and more Vue as a front end.

I have learnt symfony back-end from your courses and now is a time to learn a front end from you where I am still struggling a bit.

Cannot wait for Vue 3 course. Good job lads.

Reply

Hi Peter! I'm glad you liked our course! :) This course has a second part in the works and the Vue3 course is also in our plans! So there's a lot of Vue stuff in the future for you to get your hands on!

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
    }
}