Vue JS 3: Composition API (with Pinia & Vite) | Danny Connell | Skillshare

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Vue JS 3: Composition API (with Pinia & Vite)

teacher avatar Danny Connell, Teacher & Indie App Developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Welcome!

      1:39

    • 2.

      Introduction [Module 1]

      8:02

    • 3.

      What is the Composition API?

      5:36

    • 4.

      Editor & Software Setup

      7:44

    • 5.

      Vue Devtools

      2:07

    • 6.

      Vue 3 Docs & Install Node.js [Module 2]

      1:05

    • 7.

      Create a Vue Project

      2:38

    • 8.

      Project Setup

      3:43

    • 9.

      Options API vs Composition API [Module 3]

      1:34

    • 10.

      Options API - Data & Methods

      1:59

    • 11.

      Convert it to Composition API

      1:27

    • 12.

      Composition API - Data (refs) & Methods

      3:20

    • 13.

      Script Setup - An Easier Way!

      2:51

    • 14.

      Refs [Module 4]

      1:27

    • 15.

      Two-Way Data Binding

      1:31

    • 16.

      Reactive Objects

      2:59

    • 17.

      Non-Reactive Data

      1:20

    • 18.

      Methods [Module 5]

      3:40

    • 19.

      Computed Properties

      4:14

    • 20.

      A Note on Filters

      1:32

    • 21.

      Watch

      3:41

    • 22.

      Lifecycle Hooks - Mounted [Module 6]

      4:18

    • 23.

      Activated Hooks

      2:00

    • 24.

      Updated Hooks

      1:31

    • 25.

      Multiple Hooks!

      2:12

    • 26.

      Local Custom Directives [Module 7]

      3:48

    • 27.

      Global Custom Directives

      2:50

    • 28.

      Vue Router - $route - Part 1 [Module 8]

      3:49

    • 29.

      $route - Part 2

      3:21

    • 30.

      useRoute

      2:40

    • 31.

      useRouter

      3:29

    • 32.

      Lists (v-for) [Module 9]

      2:53

    • 33.

      Template Refs

      3:03

    • 34.

      nextTick

      1:51

    • 35.

      Teleport - Part 1

      4:23

    • 36.

      Teleport - Part 2

      3:21

    • 37.

      Child Components [Module 10]

      2:51

    • 38.

      Fix Lazy-Loading Views

      1:35

    • 39.

      Slots

      3:34

    • 40.

      Props

      3:51

    • 41.

      Emits

      3:52

    • 42.

      modelValue

      2:24

    • 43.

      update:modelValue

      2:38

    • 44.

      Dynamic Components - Part 1

      2:46

    • 45.

      Dynamic Components - Part 2

      2:19

    • 46.

      Provide / Inject - Part 1

      4:10

    • 47.

      Provide / Inject - Part 2

      3:13

    • 48.

      What is a Composable? [Module 11]

      4:54

    • 49.

      Create a Composable

      2:39

    • 50.

      Use Our Composable

      3:26

    • 51.

      Reuse our Composable

      4:15

    • 52.

      Add Composable from VueUse

      3:27

    • 53.

      What is State Management? [Module 12]

      5:34

    • 54.

      Composable State vs Vuex vs Pinia

      5:02

    • 55.

      Pinia - State - Part 1

      2:41

    • 56.

      Pinia - State - Part 2

      5:41

    • 57.

      Pinia - Actions

      3:40

    • 58.

      Pinia - Getters

      2:42

    • 59.

      Pinia - Use our Store Anywhere

      2:22

    • 60.

      Class Project: Noteballs [Module 13]

      2:06

    • 61.

      Create Project

      2:14

    • 62.

      Router - Install & Setup

      3:02

    • 63.

      Router - Add Some Routes

      4:00

    • 64.

      Add RouterView & Navigation

      1:50

    • 65.

      Router - Tidying Up

      2:19

    • 66.

      Bulma & Design - Install Bulma [Module 14]

      3:09

    • 67.

      Nav Bar - Design

      2:46

    • 68.

      Nav Bar - Navigation & Logo

      2:53

    • 69.

      Nav Bar - Responsive Design & Menu

      5:05

    • 70.

      Pages (Design)

      2:04

    • 71.

      Notes (Design)

      1:51

    • 72.

      Add Note Form (Design)

      2:30

    • 73.

      Notes Array (Ref) [Module 15]

      2:37

    • 74.

      Add Note Method

      8:59

    • 75.

      Child Component - Note

      2:32

    • 76.

      Props (Note)

      1:34

    • 77.

      Computed (Note Length)

      5:45

    • 78.

      Delete Note (Emit)

      6:24

    • 79.

      Pinia - Setup & State [Module 16]

      4:20

    • 80.

      Use Our Store

      3:40

    • 81.

      Action - Add Note

      3:47

    • 82.

      Action (with Parameters) - Add Note

      2:38

    • 83.

      Action - Delete Note

      5:15

    • 84.

      Edit Note Page & Route

      4:38

    • 85.

      Reusable Component - AddEditNote

      3:57

    • 86.

      Hook up with modelValue

      5:32

    • 87.

      Fix the Focus

      5:04

    • 88.

      Custom Color, Placeholder & Label Props

      10:31

    • 89.

      Getter - Get Note Content (useRoute)

      5:35

    • 90.

      Getter (with Parameters) - Get Note Content

      3:09

    • 91.

      Action - Update Note

      6:24

    • 92.

      useRouter - Redirect to Notes Page

      1:20

    • 93.

      More Getters & Stats Page

      7:33

    • 94.

      Directive - Autofocus [Module 17]

      2:07

    • 95.

      Global Directive - Autofocus

      2:57

    • 96.

      Watch the Number of Characters (Watch)

      2:10

    • 97.

      Composable - useWatchCharacters

      4:24

    • 98.

      Composable - Multiple Parameters

      2:48

    • 99.

      Click Outside Composable (VueUse, Template Refs)

      6:10

    • 100.

      Delete Modal Design (Reactive Objects)

      7:19

    • 101.

      Hide the Delete Modal (modelValue & update:modelValue)

      5:21

    • 102.

      Delete Modal - Click Outside to Close

      3:34

    • 103.

      Delete Modal - Keyboard Control (Lifecycle Hooks)

      6:18

    • 104.

      Delete Modal - Delete The Note

      4:57

    • 105.

      Course Roundup

      2:22

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

319

Students

1

Projects

About This Class

If you’re already familiar with Vue 2 & The Options API, then this course will teach you everything you need to know to switch over to (and get started with) Vue 3 & the amazing new Composition API.

My name’s Danny, I’m an Indie App Developer & Creator of Fudget, the highest rated personal finance app for iOS, Android, Mac & Windows.

And I’ve spent the last 12 months creating Fudget 2 - which is built on Vue 3 & The Composition API.

In this course you’ll start by learning the key differences between the Options API & Composition API by creating a simple Options API app & converting it to the Composition API.

You’ll then master all of the basics including:

  • Reactive data with Refs & Reactive Objects

  • Methods, Computed Properties & Watchers

  • Lifecycle Hooks

  • Directives

  • Vue Router

  • Child Components - including the new ways of handling props, emits & modelValue

  • Dynamic Components

  • Composables - how to create them from scratch & how to import them from the VueUse library

  • And you’ll learn State Management using Pinia, the incredible successor to Vuex

After learning the basics, you’re gonna create a real world app called Noteballs from scratch - which has full CRUD capabilities, uses Pinia for State Management and demonstrates real-world use of all the basics you learned earlier.

After this course, you’ll be able to create your own Vue 3 apps based entirely on the Composition API - from scratch.

This course requires a basic understanding of Vue 2 & The Options API, HTML, CSS & JavaScript.

Please check out the preview videos & I look forward to seeing you in the course.

Finished Source Code from Videos in this class:

Meet Your Teacher

Teacher Profile Image

Danny Connell

Teacher & Indie App Developer

Teacher

I spent 7 years working for various companies in the UK as a Web Developer (both front- and back-end) becoming highly skilled in HTML, CSS, JavaScript, jQuery, PHP and many more.

After becoming more interested in apps, I worked for 2 years as a Hybrid App Developer, creating cross-platform apps for the charity sector using technologies including Angular, Ionic, Vue.js and more.

I created my own successful app called Fudget (for iOS, Android, Mac & Windows) which is the highest user-rated personal finance app on iOS with over 1.5 million downloads. This eventually enabled me to leave my job and become a full-time Indie App Developer, working on my own apps.

I have a young but successful YouTube channel where I also share coding tutorials.

I'm super excited to ... See full profile

Related Skills

Development Web Development
Level: All Levels

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Welcome!: If you're already familiar with U2 and the options API, and this course will teach you everything you need to know to switch over to and get started with V3 and the incredible new composition API. My name is Danny, I'm in India app developer and create a budget, the highest rated personal finance up for iOS, android, Mac, or Windows. And I've spent the last 12 months creating 42, which is built on U3 and the composition API in this course, you'll start by learning the key differences in the Options API and the composition API by creating a simple options API app and converting it to the composition API. Then you'll master all of the basics including reactive data with rafts and reactive objects, methods, computed properties and watches, lifecycle hooks, directives, view router, child components, including the new ways. Finally, crops emits and modal value dynamic components composable how to create them from scratch and how to import them from the view use library and you'll learn state management using penny on the incredible successor to view x after learning these basic. So you're gonna create a real-world app called note balls from scratch, which has full crowd capabilities. It uses penny or false state management and demonstrates real-world news of all the basics that you learned earlier. After this course, you'll be able to create your own new three apps based entirely on the composition API, completely from scratch. This course requires a basic understanding if you tune the options API, HTML, CSS, and JavaScript, please check out the preview videos and I look forward to seeing you in the course. 2. Introduction [Module 1]: Hi there. My name is Danny Cano. Welcome to this course, Vue.js three composition API with penny on VT. If you're already familiar with V2 and the options API. And this course will teach you everything you need to know to switch over to and get started with B3 and the composition API. Here's roughly what we're gonna do in this course. In Module one, I'm going to introduce you to the course. Explain what the composition API is. Show you my editor setup, which is VS code, including installing all of the extensions that I'm gonna be using. I'm going to install Vue dev tools to help us with our development. In Module two, we're going to introduce you to the V3 documentation site. We're gonna install NodeJS so we can get started creating V3 apps. And we're going to create a view project using the latest build tool with VTE. Then we're going to get that project ready for us to start learning the basics. In module three, we're going to look at the key differences between the options API and the composition API. We're going to create a very simple options API app to remind us how the options API worked. Then we're going to convert that simple options API app to use the Composition API instead. This will give us a really quick and simple way to see the key differences between the two approaches. Then we're going to look up the differences between the two different patterns we can use with the composition API, which is the older setup function pattern and the newer and superior script setup pattern. In module four, we're going to learn all about data. We're going to learn about reactive data including refs, reactive objects. We're going to learn about two-way binding and we're gonna look at non-reactive data. Module five is all about methods. We're going to learn about methods, computed properties, and watches. In module six, we're going to learn all about how we use lifecycle hooks differently in the composition API, including melted hooks, activated hooks, updated hawks, and how we can use multiple hooks of the same type within a single component. In module seven, we're going to learn all about directives in the composition API. We're going to learn how to create local custom directives, which are local to an individual component. Then we're going to look at how we can create global customer directives, which can be used by any of the components in our app. In module eight, we will learn all about how we use Vue Router differently in the composition API, including how we can use the dollar routes, objects it to display information from our router in our template and the brand new US routes and use router composable. In module nine, we're going to learn about lists using v4, the amazing new teleport component. We're going to learn how to use template refs so that we can access elements which are in our component and then do something with that element when our component has loaded, such as focusing an input, we're also going to learn how to use next tick using the composition API. Module ten, we're going to learn all about child components, including how we can pass content to a child component using slots. It can pass data to child components using props. How we can emit a customer event from a child component to a parent component using emits, how we can get a child component to modify a data property on its parent component using modal value and update model value, we're going to look at dynamic components which allow us to switch out the component which is being used in a particular part of our app. And we're going to look at how we can provide data from a parent component to all of its descendant components using provide injects. Module 11, you'll learn all about composable, which is one of the most exciting new features with B3, we're going to first answer the question, what is a composable? Then we're going to create a custom composable which will allow us to share reactive data, unrelated methods across our components. Then we're gonna install and sets up a composable from the view library as well. In module 12, we're going to learn all about state management using Pena, which is currently the gold standard for state management in v3 composition API apps, we're going to first answer the question, what is state management? We're going to look at three different approaches to state management in a view three up using either composable a few pennies. And we're going to look at the differences between these. Then we're going to learn all about state with Penn. State is where we store all of our data properties, a video store. Now we're going to learn all about actions with Penny. And actions are methods which are in our store which allow us to modify the data that's in our state. Then we're going to learn about getters, which allow us to get some data from our state, modify it in some way, and then make that data available to all of our components. By this point, we will have covered all of the basics of Fe3 and the composition API. I'm going to put all of that knowledge into practice by creating a real-world app called Nope balls, which is a note-taking app which we're gonna make look responsive and presentable using the Bohmer CSS library. I'll introduce you to the note Paul's app. Then we're going to create a brand new project using the latest view build tool. Again, using V8, we're going to install Vue Router manually completely from scratch and set it all up from scratch. Then in module 14, we're going to work on the design of our app using the CSS library. We're going to install Bohmer, create a beautiful navbar design which is responsive, which shows straight up links to A2 pages on desktop, will then shows a burger menu on mobile. And then we're going to work on the design of our notes page. In module 15. I'm going to add some data methods and child components to our app. We're going to set up the data and methods for adding a new note. We're going to create a child component for our notes. We're going to pass data to that child component using props. We're going to setup a computed property and we're going to add the ability to delete a node. Module 16. We're going to install a penny a set it all up completely from scratch. And we're also going to create a reusable component, which we're going to use on multiple pages in our app. So after we've installed pennies, we're going to set up the states for our app where we're gonna store all of our notes data. Then we're going to set up some actions for adding, deleting and updating a note. Then we're going to create a getter in our Penny's door for getting the content of a note based on a note's ID. Then we're going to create a second page, a Stats page, which will display stats which are based on our notes data. I'm going to set up a couple of new getters to do this. We're also going to create a reusable component, which we're going to use on multiple pages in our app. We're going to make this reusable component customizable using props. In module 17, we're going to add some directives, watches, and composable to our app note balls. We're going to add a directive which will allow us to auto-focus a text area. I'm going to make this directive global and use it on multiple pages. We're going to set up a watcher. We're going to create a custom composable which will allow us to share reactive data and related methods across pages. Then we're going to improve that composable by allowing it to accept multiple parameters. And we're also going to add a composable from the view use library, the click outside composable. Finally, in Module 18, I'm a delete modal to our app, which will be displayed when we try to delete a note. And we'll prompt us to confirm, we're going to set up the modal, get it looking presentable. We're going to allow this modal to modify data on its parent page component by using modal value and update model value, we're going to reuse the click outside composable from view use so that we can click outside of this modal to close it, we're gonna set up some keyboard control pyramidal so that we can hit Escape to close the modal on in the process of doing that, we're also going to make use of some life cycle hooks. 3. What is the Composition API?: The composition API is the biggest new feature that came with V3. But what is the composition API? Well, it gives us a new way to create view components, an alternative to the Options API. Now we can still use the options API in v3 ups, but I would consider using the composition API exclusively, especially on more complicated apps and view components. The composition API solves two main problems that we sometimes see with options API apps, especially more complicated ups. Number one, it allows us to more easily group relevant code together in the scripts sections of our view components. And number two, it allows us to reuse our code more easily in our reactive data and our methods and watches, etc, using composable. To demonstrate this first, Let's take a look at this Options API code. In the Options API we have our default export, and within that we have all of our different options separated out by options. I mean things like data methods, lifecycle hooks. In this example here we have two completely unrelated sets of data. We have a username property out of property for determining whether or not a modal is shown. Then in our methods option, we have a method for updating the username and a method for showing the modal by changing this data property. We also have a lifecycle hook, the mounted hook, which will fire both of these methods. However, all of the relevant code is separated out. We have the username appear. The method for the username down here. The trigger of this method in this melted Hawk Down here. This means that in more complicated components, we have to do lots of scrolling up and down to data methods, computed properties, lifecycle hooks in order to work on code which is related. The composition API solves this problem by allowing goes to remove all of these options and allowing goes to group all of our code together logically. Let's take a look at a composition API version of this code. In this example, we're doing exactly the same thing. We're setting up reactive data variables for our username and our modal shown dates properties with setting up methods for manipulating these data properties were still triggering these methods in the mounted hook here and here. Except this time all of our relevant code is grouped together. All of the code related to the username grouped together here. On all of the code related to the modal is grouped together here with the composition API is not just data properties, methods and lifecycle hooks. We can group together, we can group together everything, whether it's computed, properties, watches, directives, etc. This makes our lives much easier, especially when we're working on much more complicated and longer view components. The second problem, not the composition API solves, is that it makes it much easier for us to reuse code across our components using composable in the Options API, we could share code across components using mixins. Let's take a look at our options API code example. Again, let's say we want to reuse username data property and our update Username method across multiple components in the Options API, we could extract this code into a mixin, such as this, where we've literally just copy the username data property on the octet username method and put it into a mixin. We can then import this mixin into a component or a bunch of different components like this, import mixing username from etc.. But in this example here we're also importing different mixin which is unrelated. This mixing modal mix in. And you can see that in the MTSU talk where firing method from the mixing username, this update Username method. The problem is that it's not obvious where this method is coming from. Is it coming from the username mixing or is it coming from the modal lakes in if you're not familiar with the project that you're currently working on, you might not be able to tell where this method is coming from without actually opening up these mixing files and I'm looking through the code. Again, this can become a huge problem in more complicated view apps where we have many different components, many different mix-ins being used. Let's take a look at our composition API example again. Let's say we want to do the same thing. We want to extract the username data variable, and we want to extract this update Username method where we can cut these out and paste them into a composable, which would look something like this. In a composable we just export function. And you can say within that we have our username data variable and our update Username method as well. And we can then import this composable into any view component we want. Like this, we just import the root level function which we exported. Then we can use the structuring to extract only the stuff that we need from that composable. In this case, we're extracting the username data variable and the update Username method. Now when we use something from our composable, such as in this mounted hook, we're firing the update Username method. We can see exactly where this method is coming from. So again, this becomes a massive advantage with the composition API, especially on much more complicated apps and view components. Hopefully this explains the massive advantages which come with the composition API over the options API. 4. Editor & Software Setup: You can use any editor you want for this course, but I'm gonna be using VS Code because it's free and it has a bunch of different extensions which are going to make our lives easier. If you want to have the same setup is made, then follow along. You want to go to code dot Visual Studio.com. Just download it. If you're on Windows, you might need to run an installer, but on a Mac, it'll give you a zip file with an app that you can just drag into the applications folder. I'm just going to get the Applications folder and drag this up into there. We should now be able to launch it. Click on open. We can now see the default installation of VS code. I'm just going to drag a view project into this just to demonstrate some of the extensions that we're gonna be using. Although you don't need to do this. But if you do have a view project, how did they just dragged it in? I'm going to drag in this view tests up, which is basically just the default view app built with the latest build tool. With that we're gonna be using later on, I'm going to click on truths and then yes. Then I'm just going to enlarge this a bit. Zoom in by pressing Command Plus allow. The shortcut might be different on Windows. Let's install some extensions. We want to click on this in here with these little squares. This is the extension store. On the first two extensions are an icon theme and an overall theme to just make the applicant bit prettier, but these are just entirely optional. So you want to search for material, I call theme. We want this one here. Click on Install. And then we want to choose Material Icon Theme from this dropdown. And now if we go back to the Explorer, you'll see that in our Explorer here we have these beautiful icons for all our different kinds of files, such as this nice little View icon fall components. Let's jump back to the extension store and I'm going to search for make apps theme. And this is the same that I use, but this is entirely optional. I'm going to click on that. Click Install. Choose makeups name from the drop-down. This is a relaxed, easy on the eyes theme with nice gentle syntax highlighting. As you can see here. If you don't like this theme, then there's a website where you can browse all the different themes. You want to go to. Vs Code themes.com. You can find until the different light and dark themes here. Just pick one that you like. I'm just going to close this, jump back to the extension store. We're going to install an extension called duplicate action. So I'm just going to search for that. It's this one here. Click on Install. What this will do is if we jump back to our explorer, right-click on a file, we now have this option to duplicate file or directory, which is really handy when you're working on view apps because you often want to just duplicate a component and then modify it. And I'll jump back to the extension store again by default, as you saw before. View components don't look very pretty. And they said, We're going to fix that with the next extension. And we want to search for view volar extension, APAC. We need to scroll down quite a bit to find the correct one. It's this one here by bro January. I'm going to install that. This has a bunch of different extensions in it per TEA, which is a code formats which I'm actually going to disable because I'm not really big fan of code formats is alternating tag which will automatically rename attacks. If we rename the opening target, rename the closing tag for us. We have some JavaScript code snippets, auto close tag, which will automatically close HTML tags, and then a bunch of others. The main ones we want here at these two at the bottom, these are going to give us beautiful syntax highlighting, language support, view snippets and stuff like that. I want to install this, and that's now installed, but I'm actually going to disable some of these extensions that it's added. I'm going to clear the search field and we could scroll through our installed extensions here. I'm going to disable prettier, which by the way, I believe can interfere with the next extension that we're going to install, but you can leave it arm if you prefer and see if it works for you. I'll disabled CSS lint because we're not going to be doing any TypeScript in this course. Lots about it. So I'll click on this Reload required button to reload the app. You'll see we now have this beautiful syntax highlighting for our view components. The last extension I'm going to install is called split HTML attributes. When we're working on view apps, we often have elements of view components with tons of different attributes, directives and click handlers, all that stuff. These can get quite long and we often want to in each attribute on a new line like this, which is really time-consuming money doing it manually. Well, this extension will do that for us. I'm going to search for split HTML attributes. It's this one with the green icon. I'll install that. I'm going to change the default settings for this extension. I'm going to scroll down this description to the settings section down here. Salts or the option will determine the order of the attributes. So you can see we're putting more important attributes like Vf and Vi. Vi model at the top and less important attributes like class ID further down towards the bottom. So I'm going to select this array and copy it. Then I'm going to jump to the Settings cog and then extension settings. I'm going to tick this box here, which will place our closing bracket on a new line, which I prefer. And then under assault order, I'm going to click on edit in settings.js. Normally lot this option forests, but it's not. So we're going to have to do it manually. A lot of comma here after this last option, out some quotes. And we're going to type in split HTML attributes dot sort order, not it's auto completed that forest. And then I'll select this array. Paste the one we copied from the settings page. Save that. And now if we jump back to this App.vue file, if I select this image tag, which is a self-closing tag, and phi are the extension with Control Alt Shift and a. By the way, you can change that keyboard shortcut if you like. You say it splits all of our attributes onto new lines instantly. It will also do the reverse as well. If I select this and phi are the extension again, it will put them back on a single line. Let's check out the salts order is working. So a lot of V model attribute at the end myVar. Hopefully if we run the extension, this V model will be placed at the top, which is, I'm just going to undo all of my changes here. Save that. I think this is all of the extensions I'm gonna be using. We're also going to be using the terminal a lot in VS Code to toggle the terminal, just press Control and Buck sick and it'll pop up like that. And you can also make it disappear as well with the same shortcut, your prompts might look a bit different, but don't worry, everything should still work. 5. Vue Devtools: The next thing we want to install is view depth tools, which will help us to debug our Vue apps, especially when we start working with penny on, later on, you want to go to crawl, accompanies little dots, more tools and extensions. An angle to this menu on the left. And then open Chrome Web Store. Search for view, dev tools. There's three different extensions here. At the time I'm recording this, we need to use the Beta version for V3 ups. But since you're watching this in the future than the release version, this one might work. So I would just suggest that you try the release version and I thought doesn't work, then try the Beta version, but I'm going to install the beta version. Click on Add to Chrome extension that's now installed. Just check it's working. Let's launch a view up in the browser. I'm going to open the project I had opened before. Jump to the terminal and run npm run dev, not didn't work. I think that's because I don't have the dependencies installed. So I'll run npm install first. Then run npm run dev command and click on this link here. It can now see this basic view app running in the browser. Launch, the dev tools. We can just go to our Chrome dev tools by going to View Developer, Developer Tools. Or you can use the keyboard shortcut. If we click on this little arrow, you should have a view option. And that's where our view DevTools live. And you can see all of our components here. We can also see data related to components. So this Helloworld component costs message property, the value of you did it. I'm just going to zoom in a little bit actually to the dev tools. And this is gonna be especially useful later on when we start working with Penn State management. 6. Vue 3 Docs & Install Node.js [Module 2]: Okay, let's get started now by creating a new view projects. First of all, let's jump to the view doc site. I'm just going to Google V3. Joe to the V3 website. At the time I'm recording this, the View Docs or about to be massively rebounds. I'm gonna be using the new version of the docs in this course because it shows us how to build a view three project with V8 using the latest build tool. To jump to the next version of the documentation, I'm going to jump to the address bar. Just changed this v3 to staging. We now see the beautiful new documentation. If you're already seeing this, then In a way you are. And then we want to click on install. Scroll down a bit. Before we can create a new Vue projects, we need to make sure we have Node.js installed. If you don't have that installed, then click on this link. You probably want to install the one on the left here. And you just want to download that and install it. Or if you prefer, you can use a node versioning tools such as nvm. 7. Create a Vue Project: Let's jump back to the view docs page and create our first view three projects with VT with the latest build tool. To create a new project can just run this command in our terminal. I'll just zoom in a bit, NPM in it, view out latest. I'm going to copy this VS Code and jump to the terminal. And again, we can toggle that with command and back sick. I'm going to paste that command in. Launch it. By the way, you want to make sure you're in the folder where you store your projects. We need to install this create view latest package. So I'll just type in y two, allow that. It's going to ask us for some options here for the project name, which will also be the folder name. I'm going to use Vue composition API dash basics because we're gonna be using this out to learn all of the basics of v3 and the composition API. I'm going to choose node to TypeScript know to JSX support. I'm going to choose yes to add Vue Router to our app later on in the course, when we create our course app, note balls, we're going to be choosing notes, this option. And I'm actually going to show you how to install Vue Router manually from scratch. But for simplicity, while we're learning the basics, I'm going to choose Yes and get the build tool to do this follows automatically. Again for opinion, I'm going to choose yes, although later on in the course, I am going to show you how to add opinion to a view three projects manually. I'm going to choose know to retest know to Cyprus, know to ES lint. You might want to consider using ES lint in your own projects. But for simplicity, I'm going to choose know just for the purposes of this course so that you don't get loads of annoying linting errors. But if you really want to choose yes, then you can do but I'm going to choose No. That's finished. You can see it scaffolded our project super quickly and it tells us how to get started. We need to cd into the folder it created, run NPM install to install the dependencies and run npm run dev to launch the app in the browser. So I'm just going to jump over to finder. I can see the folder is created here. So I'm going to drag that into VS Code. Open up the terminal again, run NPM install to install the dependencies. Let's finish and we can launch the app with npm run dev. We can see this URL here. We Command and click on that. We can now see the basic V3 and V2 starts to wrap running in the browser. 8. Project Setup: Let's simplify this project as much as possible to make it easy for us to practice the basics of v3 and the composition API. First of all, I just wanted to change the title which appears in the top, which currently says VTE up. So I'm going to jump to index.html, change the text in this title tag to view composition API basics. Save that. And we see that update instantly, going to close that. And now let's simplify our pages or our views. Currently we just have two pages or views, the home view and the view. Let's simplify the whole view first, I'm going to go to source and views, home view, dot view. I'm just going to stop this message appearing by choosing Don't show again. I'll just close that. And sometimes I'm gonna be hiding the sidebar here. If you want to hide it, you can go to View and appearance and toggle this show sidebar or you can use the keyboard shortcut. I'm just going to hide that for now. I'm going to remove this script tag. I'm going to remove everything inside the template tags and just start a div with a class of home. Inside that allot a H1 heading. Just put the text home and save that. Now we can see that it's updated. That I'm going to copy this code. Jump to about Vue dot view in the same folder. Just paste over everything in here. I'll change the class to about. Change the text in the heading to about as well. Save that. And we now have two really simple views. Now let's get rid of all this stuff at the top. And that's it. Our root view component, which is app.vue in source app.vue. So I'm going to open that up high the sidebar. How much you're gonna hide the terminal as well. We don't need to see the terminal most of the time. All I really want in our layout is this navigation here so that we can get to our pages. And that's this nav element here with these two router link components. So I'm going to copy this nav element in both of these router links. And then I'm going to select the whole of this header. Just paste those over that. Fix the indentation. We do need to make sure we leave this router view component because they allow us to display our actual views, our home view and our view. I'll save that. This is looking nice and simple. I'm going to remove the script section and save that. I'm going to remove some of these styles, but not all of them because some of them look quite pretty equal. If we drag this out, we can see that beyond a certain resolution, everything gets moved around. So I'm just going to remove the styles which are doing that. If we scroll down this style section, we can see this media query here with the min-width set to 100 to four pixels. I'm just going to collapse up by clicking this little arrow next to that. Select all of that media query. Just delete that and save it. And now if we increase the width of the browser should just stay the same, which we don't need any of the components in the components folder. Now, I'm going to drop to source components. I'm going to select everything in these components folder by clicking on the first item, holding down Shift and clicking the bottom one, and then right-click, Delete. Get rid of those. We see this arrow, but if we just reload the page than the error disappears. And we now have super simple app that we can use to get started learning the basics of v3 and the composition API. So let's get started learning the basics. 9. Options API vs Composition API [Module 3]: Let's create a really simple counts are out using the options API and then convert it into the composition API. This will help us quickly see the differences between the two approaches. And we'll do this on our home view. I'm going to jump to source views on home view, dot view. I'm just gonna get rid of this heading. I'm just going to have a div with a button and a counter. A lot, a button element with a class of btn with the text minus. Then I'll duplicate that. Change the text to plus. And then in the middle, we'll add a span with a class of counts. For now, I'll just put 0 in there and save that. Let's just thought some styles to put this in the center, make it look a bit more pretty. Let's add a style block. Will target this home div just sent to everything. So don't hold text align center. Save that. A little bit of padding. So our counselor isn't stopped right up against the nav. Set the padding to 20 pixels and save that. Let's just increase the size of these buttons and the counter and make it a little bit of space between everything. So I'm going to target the button class on the council class. Set the font size to 14 pixels. Save that. And I'll set the margin to ten pixels, save that. That's looking pretty decent. 10. Options API - Data & Methods: Let's set up some data and methods to get this up working using the options API. We need to add our script section. And inside that we need to add our export default. Let's set up our data first. For this counter, we need to add a data method which returns an object. We can put all of our data properties in here. A lot of property called counselor, set that to 0, save that. We should be able to use that in our template. Now, get rid of this 0, our double curly braces. And we'll just put counter in here and save that. If I change this now, we should see it update in the template which we do. I'll set that back to 0. Now let's set up some methods for increasing and decreasing this counter. I'm going to add a click handler to this button. This plus button. Click equals will fire a method called increase counter. Let's create this method. So after our data method, a lot of comma, and then we need to add our methods object. And we'll create this method increase counts of all we want to do is increase this counter property by one. So to access our data properties, we can just do this dots and then counsel. And we can just do plus plus to increase it by one. I'll save that and let's see if that's working yet. We can increase the counter. So now we just need a method to decrease the counselor. So a lot of comma here and I'll duplicate this method, rename it to decrease counter, just changed the code to this dot counter minus minus to decrease this council property. And then I'll copy this click handler from the plus button and add it to the minus button and just changed the method name to decrease counte and save that. And we can now increase and decrease our counter. 11. Convert it to Composition API: Let's switch this simple app over to using the composition API. And one important thing to note is that everything in the template stays the same. We don't need to change anything in the template. Generally with the composition API, everything in the template works exactly the same. We use data properties, computed properties methods in exactly the same way. The only place we do things differently is in the script section. I'm going to comment out this Options API script section. For now. A lot of opening comment at the top of it, and then a closing comments at the bottom of it. Let's add a new script section where we'll use the Composition API scripts. Now we can use the Composition API with two different patterns. There's the setup function pattern, which was the original pattern, which came on the composition API first came out. And then we have the second pattern, which is the script setup pattern, which was released later on on is much better. But for now, let's just use the original setup function pattern. Although we will like to use the superior script setup pattern. To use the setup function pattern, again, we need to add our export default. But we don't add options like data methods, etc. Instead, we add a setup function like this and we place all of our code in here. Basically all of our data properties, methods, computed properties, watches, etc, will all go inside this setup function. 12. Composition API - Data (refs) & Methods: Let's set up a reactive data variable for our counter. Now in the composition API, there are two main types of reactive data. We have refs and reactive objects. I'll, I'll get into these in more detail later on in the course. Well, basically a reactive object allows us to create an object of data with a bunch of related data properties inside the object. Whereas RF is generally used for simple single items of data, such as a string and array or a number. So it makes sense to use a rough for this, to setup a href, we can just create a constant and give it a name. Whatever name we use will be the name that's available in the a template. So we're going to call this counter, since that's the name we've used in our template here. And we want to set that equal to the ref method like this. And then we can pass our initial value inside this method. So let's set that to 0 initially because we're using this ref method, we need to import this method from view. So above our export, we need to import from view. Now this council rep is not available in our template yet. We also need to return it when we're using the setup function, pots it right to the bottom of our setup function, we need to add a return statement. This return statement should always stay at the bottom of the setup function. And then we just need to return this council ref, like this and save that. And hopefully this counter should be hoped top now we can see 0 on the page. And if we change the value of this href and save it, we can see it's updated on the template. I'm just going to set this back to 0 and save that. We now need to add our decreased counter and increase counter methods. And the way we do this is just by creating some named functions anywhere within this setup function. And then we need to return them just like we return this counter ref. So let's create a method for increasing the counter. So we can either do this like this with the function keyword, function increase. Counselor. We can do a constant named increase counter, which we set equal to a function like this, which is the method that I usually use. All we want to do is increase this counter ref by one. And you might think we'd be able to just do count plus, plus. But actually this won't work with the composition API because when we create a rough, they actually creates an object and the value of our ref is stored in a property called value. So to access this, we actually need to do counselor dot value, and we can then do plus plus again, in order to use this method in our template, we need to return it. So we can just thought it to this return object like this increase counter. Since we already have OK click handler here, which is firing the same method, it should work. So I'll save that, click on the plus button and that's working. Now we just need to create the decreased counter method. I'll duplicate this increased calcium method, rename it to decrease counter and just change the plus plus to minus minus so that it decreases the counter value by one. And again, we need to return this a lot it to our return statement down here, decrease delta, save that. And hopefully both buttons should be working on yet everything is working. 13. Script Setup - An Easier Way!: In few three-point to a new syntax or pattern for the composition API was introduced, which is the script setup pattern or syntax. Script setup allows us to make our components a lot simpler, less cluttered, and easier to manage, allows us to get rid of the export default, get rid of the setup function, and most importantly, get rid of the return statement. With the scripts setup A2, we no longer need to worry about making sure all of our data properties, methods, computed properties, etc, are returned. Now on a simple component like this, it might not make much difference, but in a really complicates it up with hundreds of components which each have it sounds of different data properties, methods, watchers, computed properties, etc. It really makes our life easier not having to constantly keep this return statement updated every time we change any of our data properties, methods, or rename them, etc. For my own up, Fujitsu, which are currently working on, actually spent a whole day converting around a 100 components from this a setup function pattern to the new script setup pattern. And that was a really boring day, but it was well worth it because now the app is much easier to work on. By the way, the script setup pattern is the pattern I'm gonna be using for the rest of the course because it's much simpler and it's the pattern that the Vue team recommends. So let's convert this code to the new script setup pattern. I'm going to comment out this script section. We'll add a new script section here. Now we don't need to add our export default, and we don't need to add this setup function. All we need to do is add an attribute to this opening script tag like this. We can now place all of our code in here, all of our data properties, methods, etc. And we don't even need to return any of these. Let's just copy our two methods on our council ref from the old code. Paste it straight in here and just fix the indentation. We still need to import this ref method from view. So I'll copy that. We just place all of our inputs at the top of this script section In this. And now we don't need to return our data or our methods. Any data properties or methods are computed properties that we declare at the top level of this script tag will be available in our template automatically. So if I save this now, it should just be working straight away. Yet. It's still working. We've now reduced our script section from, let's say, 21 lines down to just 11 lines. We've massively simplified our code and we no longer need to worry about this pesky return statement. 14. Refs [Module 4]: There are three main types of data that we can use in a composition API app. Reps, which we've already added in this app, reactive objects and non-reactive data. We've already covered refs. We basically just sets up a constant or a variable set that equal to the ref method which we need to import from view and then just pass in the initial value. We can make changes to that value by accessing the value property of the constant that we've setup, we cannot as many reps as we want. So let's add another one. Let's say we want a title for our counter and we want to be able to change that title programmatically, all with two-way binding. Let's add another rep here. We could do const counts at title, set that equal to a ref method, pass in an initial value of, let's say my counter. Or we can show this by getting rid of the const keyword and then adding a comma after the first const. Again, we don't need to return this constant. It should be available in our template straightaway. So let's add H3 tag, both this div will output this title. So double curly braces, counter title, save that. We can now see that title on the template to make it a bit clearer what this title is, I'm just going to add a semicolon after this title. Now if we change the value of this graph, we should see the title update, which we do. 15. Two-Way Data Binding: For the most part, two-way data binding works exactly the same in the composition API as it does in the Options API. Just to demonstrate this, let's add an input to our page, which allows us to modify this counter title which we have stored in this data ref after this div with our counter and buttons a lot of div with a class of edit. And then inside that I'm going to add h4 heading with the text, edit counter, title, colon, and then a lot. And save that. I'm just going to add a bit of margin to the top of this div. I'll jump down to the while we're at it. Let's just remove all of these old comments. And I'll jump to the script section. Target, the edit class, which we just started to that dip, and a lot of margin, top of 60 pixels and save that. Now let's bind this input to counter, this counter title rref. So to do that, we can just jump to this input and add a V model directive. Just set it equal to this const count, the title, council title. Say that. We can now see our count the title in this input. And if we change it, it's not updating the heading up here. Maybe we just need to refresh, try that again on yet if we change the heading title in this input, we see it updated in our heading or pay it. 16. Reactive Objects: We can see that refs are handy for storing simple independent items of data, such as a number or a string, or maybe an array or a Boolean. But what if we want to store a bunch of different data that's related together in a single object. A bit like we did with the data method in Options API Apps. Well, we can do that with a reactive object. Let's say we want to store our counter and our counselor title together in a single object. Since this data is related, then we could do that with a reactive object. To setup a reactive objects, we again create a const or we can use a variable. Now we give that a name so we could call it counselor data. And we set that equal to the reactive method. We do need to import this from view. So we can just add that to our import object here, reactive. We can just pass an object into this reactive method and we can place all of our data properties in here. Let's create a property called Count for our counter, give that an initial value of 0. And then a lot of property called title, the title, and I will set that to my counter, save that. Let's use the data from this reactive objects in our template instead of the data from these refs. If we want to use the counselor that's in this reactive objects here, we can just do counselor data dot count. If we want to use the title, it's in the reactive objects. We can just change this in this heading tag to Counselor data dot title. Let's update this V model as well. To use this reactive objects. I'll set this V model to counsel data, dots, title, and I'll save that and reload. And let's see if our title is sopped up. Okay, yeah, that's still working on the two-way data binding is still working as well and accounts or is working, we can see 0 displayed there. And if I change the value of counter data dot counts, we can see it update. However, these bottoms are not working. And that's because these methods, the increased council method and decrease counselor method, are still manipulating this href and not discount that's in our reactive object. So let's update these methods. Instead of counts or dot value plus plus, we can just do counselor data, dot count plus plus. Then in the decreased council method, we can just do counts of data, dot count minus minus. Note that we don't need to use dot value when we're using data that's in reactive objects, we just use the straight property name, counselor data dot count. Now let's save that and see if these buttons are working on. Yeah, that's working again now, we're no longer using these refs anymore. Vs code shows us that by graying these out a little bit. So let's just comment these out. Since we no longer using this ref method from view, we can remove that from this import statement like so and save that. 17. Non-Reactive Data: Sometimes in our view components we want to have a data property that doesn't need to be reactive. This is easy to do with the composition API. And I would recommend that any data properties in your component that don't need to be reactive, you should make non-reactive as this will improve the performance of your app. Let's say we want to store the title of our app in a data variable, but we don't need it to be reactive. We don't need to be able to change the value of that title programmatically and see it updated on the template. All we need to do is setup a constant or variable. So I'll create a constant called title. Just set that equal to a string. So I'm not using a reactive it just a bog standard constant which is set to a string. I'll set this string to my amazing Kaltura up and save that. This constant should now be available in our template. I'll jump up to the template inside this div with the class of home. H2 will output this constant, so double curly braces. And then outside, it'll save that. And we can now see our title on the page. And if we change this string to my okay, counteract, which has a bit more apt, then we see that update on the page. 18. Methods [Module 5]: We've already learned how to add methods to our component. Let's just quickly go over how we can pass parameters to methods. First, I'm just going to remove these comments from before. So let's say we want to be able to increase our counter by a specific number rather than just by one. Well, we could pass in the number we want to increase it by a parameter on our increase counterparts. In here, we can just start parentheses to our method name passing a value. Let's just pass in one for now. And if we jump down to our increased counter method, we can now receive that parameter like this. As a shorthand, we could actually just remove these parentheses if we want to. Just log this out to make sure it's coming through console.log and amount. Save that. I'm just going to open up the Chrome DevTools. You can either go to View in Chrome Developer Developer Tools or you can use the keyboard shortcut onto the console down here. If I click on the increased countable, yeah, we can see this number one being locked out. Let's adjust the logic and our increased counter method so that it increases the count in our reactive objects by the amount that we're passing in instead of just by one. So we can either do counts of data dot count equals count the data, dot counts plus amount, or as a shorthand, we could do counter data dot count plus equals amount. I'll save that. Make sure that it's still working. Yet, still working. Now we could add a second Boltzmann for incrementing the counter by two. I'll jump back up to the template and I'll duplicate this increase counter button, but we'll pass in to this method. I'll just change the text in this button to plus, plus. Save that, and I'll just zoom out a little bit. Now when we click this button, we can see our counter is increased by two. Well, this button is still increasing it by one. I'll just scroll down and remove that console.log. Let's do the same for our decreased cancer. I'll duplicate this decreased Counter button. In the second button we'll pass in the value one, and then in the first button will pass in the value to change the text in the button to minus minus. If that, we need to update this decreased counselor method. So again, we can do Council data dot counts equals counts of data dot count minus amount. Or we didn't need to pass in the amount fossa in here. Or as a shorthand, we can do Council data dot count minus equals amount. So let's save that and see if that's working. Just drag the console over a bit. This button is decreasing the counter by one. This button is decreasing it by two. By the way, if you want to get access to the event object, then we could do that by just passing a second parameter to our method. So I'll pass it to this one here. On this needs to be named Dollar event. And now if we jump to our increased counselor method will need to put the parentheses back in. Since there's gonna be two parameters, we can then just pass in the event object like that. You can use any name you want, but I'll just use a. Now we should be able to log this out. And then when we click this increased by one voltage, we can say the event object being logged out. And we can then get access to what element was clicked on, whereabouts the cursor was, etc. Before we move on, let's just remove this console.log. 19. Computed Properties: Computed properties are properties which are usually generated based on reactive data, which are cached and only updated when there are dependencies change. For example, we could create a computed property which takes the value of this counter, manipulates it somehow. The value of our computed property will only be regenerated whenever the counter changes. So let's just remind ourselves how we added computed properties using the options API. I'll jump to the bottom of this script section and add another script section. The export default. The options API. We had to add a computed option. We had to place all of our computed properties in here, such as my computed property. Then within that we would perform some logic based on a data property. Then we would return something. This meant that all of our computed properties had to be lumped together in this one computed object. However, with the composition API, we can create a computed property literally anywhere within our script section. This is really helpful, especially on larger components because it means we can group all of our relevant code together, which is something that I demonstrated back in module one. As I said back in module one, this is one of the main advantages of the composition API. The fact that it allows us to group all of our related code together, whether it's data properties, methods, computed properties, watches, lifecycle, hooks, et cetera. Let's just comment out this Options API example here. We'll add a computed property to our composition API code. Now to create a computed property, we do need to import the computed method from view. We can just start that to our input here, comma computed. And now let's setup a computed property which determines whether the counter is odd or even. And then space this out on the page. First, let's just set up the narco after this div with all our buttons and the counter, a lot of paragraph tag, and I'll just add the text. This counter is odd. Hello, We're gonna make this word odd dynamic using our computer property. I'll save that. Let's scroll down a bit. And again, we can create our computer property anywhere we want. I guess it makes sense to place it after a reactive object without data. To create a computed property, we just need to fire the computed method. Within that, we just need to pass in a method like this. Then we need to return something. In order to make use of this computer property, we need to assign it to a constant or a variable. So I'll assign this to a constant called odd or even. Oops, I spelled that wrong. Odd or even. We can use the remainder operator to work out whether a number is odd or even. So we can just do IF and then to access our counts. So we can just do counts of data dot count. If counter data dot count, remainder two is equal to 0. In other words, if we divide our cout value by two and get the remainder, well, if it's an even number and we divide it by two, then the remainder will be 0. And we know that the number is even. If this is the case, then we can return even. Otherwise. Else we can just return odd. Now we can actually just get rid of this else if we want to sense it will never actually gets to this line. If this first line is true, I'll get rid of the word else on Save that this computed property is now ready to use in our template. I'll jump up to the market we just started this paragraph. Remove the word odd, an odd double curly braces, just output our computed property odd or even. Save that. We can see the counselor is 0 and it says this counter is even. We increase it by one. And it says discounts or is odd. We set it to two and it's even again, etc. 20. A Note on Filters: If you're wondering how to do filters with V3 on the composition API, well, they've actually been removed from V3. If you just Google view three filters on joke to this page from the migration guide, we can see that they've actually been removed. In view to, we could add a filters object to our default export. And we could create a filter which would accept the value and then return something else. And then we could use that filter in our template by just adding a pipe followed by the name of the filter. If we scroll down to the a3x, update, impulses are removed, are no longer supported. Instead, we recommend replacing them with method calls or computed properties. Now this isn't a big problem because we can easily achieve the same functionality as a filter by using computed properties or methods. Fact, what we've done here with this odd or even is kind of like a filter. We're taking a value and outputting something based on that value. But one of the handy things about filters walls, we could easily make a global filter, use it throughout our app. So let's say we had an app which displayed currency on lots of different pages. We could create a filter which would convert a number into a formatted currency string with a dollar at the start and maybe some commas to separate the zeros. However, if V3 we can achieve the same thing by creating a computed property or a method of making that computed property or method global by using composable. And then we can easily use that functionality anyway, I within our app. And we'll learn more about this later on in the course when we cover composable. 21. Watch: Watches allow us to essentially watch a reactive data property and they do something whenever it changes. Let's just remind ourselves how we did that with the options API. So I'm gonna move this closing comment up here. Let's say we had a data method. Within that we had a count property set to 0 initially. And then we want to watch this count and let's say do something when it hits a particular value, where we would have to add a watch option, object to export. And then within that, we could add a watcher to watch these count like this. We could create a method called count. We can pass in two parameters, the new count on the old count. Then we can do something whenever count changes, we could do something like if you count is equal to 20, then we could alert message. Again, this meant with the options API that all of our watches have to be grouped together inside this watch object. And this meant that these watches are often many, many lines of code away from the actual data that they're working with. However, with the composition API, we can setup our watches anywhere we like within our script section, which means we can easily group watches with their relevant data. I'm just going to drop this closing comment and place it at the bottom of the script section again, let's do this. The composition API way less sets up a watcher, which is our counts and our fires an alert when it reaches 20. To use a watcher, we do need to import the watch method from view. And it makes sense to place our watcher after the data that we're going to be watching. So we'll place that hit to create the watcher. We just want to fire the watch method. The first parameter should be the reactive data item that we're going to watch if our council was in a ref, such as const counts equals ref like that, then we could just pass in count the first parameter. While since our counter is in a reactive object, it's actually a nested data property. And we can't actually do counselor data dot count. That's not gonna work. So we have to use a getter instead to actually grab this nested data property for us. And we could do that like so we can just do parentheses and then an arrow symbol, and then we can just add the nested data property that we want to grab, which is counselor data, dot count. And then for the second parameter we add a method, parentheses and then arrow curly braces. And again, we can access the new value and the old value here. So new counts, old counts. I'll just get rid of this count REF that we added. And so we should be able to lock these out now, a logout, new count, save that, reload the page. And if we change our counter, we can see our watcher is being fired and we can see new count one in the console. And every time it changes, watcher will be fired again. Let's show an alerts if our counter hits 20. We can just do if new count is equal to 20, alert. Way to go. You made it to 20. Since we're not using the old count, we don't need to actually declare it here, so I'll get rid of that and save that. And let's see if that's working. So hopefully when we get to 20, we say an alert. 22. Lifecycle Hooks - Mounted [Module 6]: Lifecycle hooks allow us to execute code at different stages of our components lifecycle. We can execute code when a component is mounted, as in when it's loaded into the browser. Or we can execute code when it's unmounted as an unloaded from the browser. And let's just remind ourselves how we use lifecycle hooks in the Options API. I'm going to scroll down to the script section again. Just caught the closing comment and paste it at the top. In the Options API, we would add hooks like this. To add a mounted hook, for example, we would just add a mounted method to our default export like this. Then we could do stuff. When component is loaded. We could add a unmounted hook like this. Do stuff when the component is unloaded from the browser. And now let's just stick a couple of console logs in here. Console.log mounted and console.log on mounted and save that. And this will still work by the way, we can actually combine the composition API with the options API. You'll see if I reload the page, we can see mounted being locked out in the console. And if I leave the page, you can see unmounted being locked out because our home view has been removed from the browser. However, I wouldn't recommend using both the composition API and the options API at the same time. Number one, it makes our components really messy and inconsistent. And number two is difficult to communicate between the different scripts sections. For example, in our options API code, we cannot access data properties which are in composition API script tag. But anyway, because with the options API, we could only add one hook of each type, one mounted hook, and one on mounted hook. This meant that we often need it to bundle a lot of unrelated logic altogether in these hawks. But in the composition API we cannot as many hooks as we like, as many mounted hooks as we like, and as many unmounted hooks as we like. And we can place this anywhere we want to within our scripts section. Let's just comment out this Options API script section. Again. Let's add all of the mounts it hooks to this component. Lot these in order of execution. The first one is the before mount hook, which will be fired just before the component is loaded to the browser. To do this in the composition API, we just thought a method called on before mount. And then we pass a method into this method, we can stick all of our code here that we want to fire before the component is mounted. So I'll just log out on before and then I'm going to duplicate this. And next we have the unmounted hook to add like this. Then we have the R&B for unmounted hook. So before unmount. By the way, I'm just doing a multiple selection here by holding down the Alt key, selecting the texts, holding down the Alt key and then selecting the other texts. Finally, we have the unmounted hook. For this, we add a method called unmounted. To use these hooks, we do need to import them from view. So I'm gonna do a multiple selection here and select all of these method names and copy them. And jump to our import, add a comma and just paste those in. And then I'm just going to join this back together by pressing Command Shift and pay to show the command palette. I think the shortcut is different on Windows, just firing the join lines command. Then I'll just start some commas between these. Let's save that and see if it's working. I'll reload the page. And yeah, we can see on before mount being locked out and then on mount it being locked out. And if we jump to the about page, we can see on before on Mount being locked out and on unmounted being locked out. One thing to know is that there are no created or before created hooks in the composition API. And that's because code that we put at the root level of script tags will effectively be fired at this point. So all of this code in our script section is effectively being fired before the component is created. 23. Activated Hooks: Let's see how we cannot activate it on deactivated hooks with the composition API. We do this in the same way. I'll just duplicate this on mounted hook. We just add on activated hook, we'll just log out on activated. And then for the deactivated hook, we just use the deactivated. Again, we need to add these to our import or it looks like VS Code is automatically added. These, just add them here. If it didn't. I'll save that now these hooks will only be fired if our components being kept alive. That means that the component keeps running in the background, even when it's not being displayed on the page. To do this, we need to wrap our router view component in keep-alive tags. We want to jump to app.vue. We want to wrap this router view in keep-alive tags. We can either use dash case like this. We can use Pascal case. I'll use Pascal case since that's what we're using everywhere in our AP. Just want to stick this router view inside these tags. Save that. Actually we do this differently in V3 because we see this warning here. Router view can no longer be used directly inside transition or keep alive slot props instead. And it gives us a little example here of how to do that. We can just copy this, paste it over here. It should now keep all of the components which get loaded into this router view alive and we should see are activated and deactivated Hook's work in. So I'll save that and reload the app. We can see on activated there. If we leave this page, let me see the on deactivated hope being fired as well. Now I don't want to keep the pages alive in this up, so I'm just going to undo everything we changed in app.vue so that we just have our router view component on its own. And then I'll save that. 24. Updated Hooks: We also have the updated hooks which are fired whenever our template changes. So for example, whenever our council changes, Let's add those hooks to this home view dot Vue component. And this is a bit messy now. So I'm going to remove all of these hooks and remove the inputs of these hooks. Going to add a on before update hook. We'll just log out on before update. This hook will be fired just before the template is updated. So if we click on this button on the counter changes, this will be fired just before the template is updated. And then we have the updated hook which will be fired at the point when the template is updated and we'll just log out on update. Actually, my BOD is not all update, it's all updated. So I'll just fix that. Again. We need to import these. Will then pull on before updates on, updated from view and save that. If we reload, then you can say these hooks are not fired when the component is first loaded. If I click this button and the counter increases and our template changes, then we can see these hooks being fired first the on before update hook, followed by the updated hook. 25. Multiple Hooks!: Before we move on, let's just demonstrate adding multiple hooks of the same type. I'm just going to remove these updated hooks. I'll remove the inputs. Remove the natural hooks. Let's say we want to do some stuff related to our app title when the component is first loaded. We also want to do some stuff related to our counter when the component is first loaded as well. So we can just stop to unmelted hawks. Let's import on mounted from view. And then after our app title, we cannot be unmounted hook. I'll just log out, do stuff related to Title. And then let's say we want to do some stuff related to the counter when the component is mounted, then we might want to add that down here. So we can just paste in another talk. I'll just change the text in this log to do stuff related to counter. Save that. This way with the composition API, we can keep all of our hooks together with related code, especially if we use comments to separate out our different sections of code. What I often do in view components is I add a block comment like this. I'll just add a comment for our inputs. Then move this up. Indent. And then a lot, a little comment for our app title. We'll put the app title calls and the unmounted hook related to it underneath this comment. And then a lot of one mole block comment for the council. I will place all the code relates to our council underneath this comment. And I'll save that. Even though this component isn't especially complicated, we're already starting to see the benefit that the composition API gives us in terms of keeping our related logic altogether, instead of scattered out across the various options, which we have to do when we were using the options API. 26. Local Custom Directives [Module 7]: Vue.js is followed. Directives out of the box such as V model, V show, v-if, etc. But we can create our own custom directives to add functionality to elements so that we can do something to an element once it's been created or mounted. Classic example is to create a directive which autofocus is an input field. So let's create a direct save which also focuses this import on our home view. I'm going to jump to the home view in source views and home view dot view. And let's remind ourselves how we do this. The options API way. I'm going to uncomment the options API code. The options API. We had to add a directives option, which is an object. We could place all of our directives in here. Let's create an auto-focus directive. So we could call this autofocus. And again, we set this to an object. Inside here we can add any hooks we want it such as created or mounted. So let's add a mounted hook. We can get access to the element by passing in this l parameter. And then we can do something with that element. In this case, we want to focus it so we can do L dot focus. I'll save that. We should now be able to use this directive on our input. And we just need to prepare and whatever name we've used here with v dash. If I jump up to the input which is here, we should be able to just add v dash, also focus. This should still work even though we're using options API here. If I reload the page now, you can see it's also focused this input with the options API. All of our local directives had to be placed within this directives objects, which again, as I keep saying with the options API, this often leads to related code being scattered across many lines of code. Whereas with the composition API, we can create a local directive anywhere within a setup function. Let's comment out our options API code and do this the composition API way. I'll just comment out this script section on Jupiter to our composition API scripts section. At the button, I'm going to add another one of these block comments called directives. To create a directive, we need to create a const. The name of this caused needs to be camelcase and it needs to start with a lowercase v. We might call this V also focus. Whatever name we use here will determine the attribute that we actually use, although it will be converted into dash case like this. V autofocus, camelCase becomes v dash autofocus when we use it on the template. Anyway, we want to set this to an object. Inside here we can add a lifecycle hook such as created. And we set this as a property which is set to a method like this. We can then do something with the element which we've added this directive to in this created hawk. And we can also use all the other books as well, such as before mounted and mounted before update. Updated before on mount. Mounted. But for this, let's use the mounted hook again. We can just use the same code we did before to focus the simple l dot focus. We've already added this directive to the simple. This should hopefully be working now, I'll just reload the page and yet it's still auto focusing this input. But now we have the freedom to place this local directive anywhere we want within our scripts section. 27. Global Custom Directives: Right now we can only use this V autofocus directive on this particular component, home view dot view. If we want to be able to use this directive throughout Sarah on different components, then we can take our directive, put it into its own file, and create a global custom directive. So let's jump to the File Explorer and we'll create a folder where we can place our custom directives. So I'm going to create a new folder inside the source folder called directives. I'm going to create a new file. And let's get this file the same name as our directive. So V autofocus dot js. And by the way, if you have issues with your tab spacing here, because all of these files in this project that we created habitat space of two spaces. You might find that when you create a new file is set to four spaces because that's the default in VS code. If you want to fix that, just go to code and preferences and settings. Just change this tap size setting to two. And you may need to just delete this file and recreate it as well, this V autofocus file. But anyway, let's move our directive into this file. I'll jump back to home view dot view. Now we can just cut this code. I'll delete the comment as well. We can jump to the autofocus JS and just paste that in. And all we need to do to make this available to other files is export this constant. So we can just start export and save that. Now if we jump back to whole view dot view, we can now import this directive and use it. I'll just jump up to the impulse section. To import this directive. We can just do import autofocus from slash directives slash v autofocus. We already have directive added to this impulse, so this should hopefully be working. I'll reload the page on yet is still work in, except this is now a global directive that we can use anywhere. Just to demonstrate that let's add a text area to our about page. So let's open up the About Page Source views about bu.edu, lot of text area. After this heading. Then we need to add script setup section, a lot of comments for imports. Then after that, I'll just copy the import code from own view dot view, paste that in there, and we should now be able to use that here as well. So let's add it to this text area v autofocus. Save that reload. And yet it's working here as well. We now have a handy global directive which we can use anywhere in our app. 28. Vue Router - $route - Part 1 [Module 8]: Let's see how we do things differently with Vue Router in the composition API. Now you may remember the dollar routes objects which we could use to access things like the current route path or route parameters in the Options API. Well, we can still use this dollar root objects in our template when we're using the composition API. In order to demonstrate that, let's create a post detail page which accepts a parameter of ID. And then we'll use the dollar roots object to display this ID on that post detail page. But before we do that, let's change this About page to a posts page, which later on we'll add a list of posts to. We want to jump to our router file in source router index.js. And by the way, I'm gonna be showing you how to set up the view router from scratch later on in the course when we create our note balls up. But in this project that's all been setup for us. We don't worry, I will be showing you how to set all this stuff up from scratch. You can understand what's going on here a little bit better if you're not already familiar with Vue Router. But anyway, let's adjust this About Page and make it a posts page. So I'm going to change the path to slash posts. I'll change the name to posts and I'll get rid of this bunch of comments. And we'll import a component called Posts view instead. I'll save that. This will break our app because it's looking for this component which doesn't exist. So let's open up our explorer, jump into views and we'll rename this about Vue dot view to posts view dot view is now working again. And now let's update our nav links at the top. These are in app.vue, source app.vue. We want to change this second router link to go to the path slash posts. And we want to change the text to posts as well. Save that, we should now be able to get to argue posts page. So let's adjust this posts page a little bit. Source views and posts view top view will change this class on this root div, two posts, and we'll change the text in this heading, two posts as well. Save that in a little bit. We're going to add a list of links to some posts. Well, first of all, let's sets up a route for our posts detail page. So we'll jump back to index.js in the router folder. And I'm going to add a comma after this posts route and then duplicate that. And we'll set the path to post detail. And we want this route, and we want this route to accept a parameter named ID. So to do that, we can just add slash colon ID at the end. This means if we go to slash posts detailed slash ID one, then the value of the ID parameter that we're passing to this route will be ID1. And then we'll change the name to post detail, and we'll change the component to post detail view, top view. And we'll save that again, this will break our app because this component doesn't exist. So let's duplicate this post's view component by right-clicking it and using the duplicate file or directory command, which is coming from the duplicate action extension which we installed earlier on in the course. We'll duplicate this and we'll call it post detail view. I'm going to remove everything inside the script tags are removed. Text area. I'll change this class to postdoc detail. Change the text in the heading to post page, save that, reload the page, and that's not working. And that's because I rename this component incorrectly. It should be posted detailed view dot view. Let's rename that again, just thought the worldview at the end. Let's add some links to this posts page so we can get to our post detail page and then you use the dollar routes. Object to access our route parameter. 29. $route - Part 2: Let's add some links to our post detail page. On this posts page, I'm going to jump to views on posts view, dot view. And after this one, I'm going to add an unordered list. Inside that I'm going to add an LI tag. And then inside that I'm going to add a router link to router link. We can use dash case like this, or we can use Pascal case. And I'm going to use Pascal case. Inside the output, the text posts one answer, set the path for this router link. We can just add a two attribute and set that to our path. And if we jump back to our router file source router index.js, the path we specified here was slash, post detailed slash and then the ID. So I'm going to set this to attribute to slash post detail slash id one. And then I'm going to duplicate this LI two times, changed the two value in the second link to id2 at the end, and then post two, and then the third one, I'll change it to three and then post three. Let's save that and we can see a link to those posts. Let's just start a little bit of margin at the bottom. A lot. A style section which is scoped, which means that any styles we put in here, it will only apply to this particular component. So I'll just add some margin bottom to the UL, margin, bottom 30 pixels. Save that. Let's see if these links are working. And yet we're on our posts page or post detail page. We can see slash id one in the address bar. And if we go back and click on the second one, let me say slash id2 at the end, we can now use the dollar routes objects in our template to get access to this ID parameter that we're passing in. Let's open up the post detail page, posts detail view, dot view. Lets out a paragraph underneath this heading. And I'll just have the text display the content of posts with ID of enamel, add double curly braces, dollar root, dot params, and then the name of our parameter, which is ID dot ID here. Let's save that. And we can say display the content of posts with an ID of id2 here. If we go back and click on the third post, then we see ID3. And let's just quickly out of back button here, I'll add another paragraph on a router link with the texts back, just to add a little left arrow here, we're going to use a HTML entity, LT, which sounds for less than and then semicolon. And then we'll set the two value on this router link to slash posts. Save that back button is not working. You can see the accessing the dollar routes objects in our template is exactly the same in the composition API as it is in the Options API. But how can we access our route's information in our scripts section? Well, in the Options API, we could just do this dot dollar root access, all of the same information. We could use this inside methods computed properties are lifecycle hooks or in the composition API, we don't have access to this. We don't have access to view instance on all of the options because we don't have any options. In the composition API, we need to use view threes, use route composable instead. 30. useRoute: We cannot use this dot dollar root in the composition API. If I save that, we see an error and our outbreaks. So let's remove that. Do this the composition API way. Let's add a button to this page which fires a method when we click it. So after this paragraph tag in-between these two paragraphs, we're going to add a div. Inside that. I'm going to add a button with the text show post ID. When we click this, we want to show a LIT which displays the post ID. Let's add a click handler to this. So I'll click Show posted. Let's create this method a lot. This method as a const, const, show posts I equals a method. For now let's just log out show post ID to make sure this is working in. Save that click on the button and we can say show post ID in the console. So now in this method we want to show an alert which displays the post ID. And again, we can't do this dot dollar root. So we need to import views new use route composable. So we need to import that first. So import use route from view router. Note that we are reporting this from Vue Router and not from view. To get access to that composable, we need to assign it to a variable or constant on the View Team recommends to use the name route. So a lot of concepts called route settler equal to US routes parentheses. We can now access this composable using this route constant. And by the way, I'll be getting into composable is in more detail later on in the course, including how to create a custom composable and how to import composable from third-party libraries such as view use. But anyway, we can now use this route constant just like we would use this dot dollar out. Inside this method, Let's show an alert. I'll use backticks to output a template string here, and we'll just output the ID of this post is Colab. Then allow dollar and curly braces so that we can output something here and we want to display our ID route parameter. So instead of this dot dollar roots dot params dot ID, we can now just do route dot params dot ID. Let's save that and see if that's working. Click on the button. We see the alert. The idea of this post is ID3, and that's working. Let's try it out on a different post, post to click on the button. The idea of this post is id2. 31. useRouter: In the Options API, we could handle the router programmatically by using this dot dollar router. For example, if we wanted to send the user to the homepage, we could do this dot dollar router dot push and then the path slash. And that would push them back to the homepage. But again, in the composition API, we don't have access to this. So we need to use the US router composable from Vue Router. Let's get rid of this line. Will import use router from view router. And again, we need to assign this to a constant which we typically call router. So I'll duplicate this constant here, change it to router, and change this to use router parentheses. Now let's add another button to our page after this div with the first button. Let's add another div. And then another button with the text go home. In three seconds. Let's add a click handler to this button. Click equals go home in three seconds. And now let's create this method after this method. So const, go home in three seconds equals an arrow function setTimeout in VS code. If you just type in set, you can choose this setTimeout function snippet, which allowed that foreigners. I'm just gonna get rid of the semicolon and we'll set the timeout to 3 thousand milliseconds three seconds. After this 3 second timeout, we want to send the user to the homepage. And so instead of doing this dot dollar router dot push, parentheses slash, we can now just do router dot push slash. Now let's save that. Click on the button and wait three seconds. Went back on the homepage. If we want to push the user based on the route name instead of this explicit path by name, I mean the nameless specified here on each of our routes in index.js in the router folder, then we can do that as well. We can just change this to router dot push and then an object and their name. And then the name of the route that we want to go to, which is home lowercase. Save that and see if that works. Click on the button. Wait three seconds. Yeah, that works as well. Or if we want to send the user to a particular post detail page with a particular ID, then we could do that as well. So I'll jump back to the post detail page, a little button. I'll just duplicate this div and button. And let's say we want to send the user to the first post. The post with an ID of ID one. I'll change the text inside this button to go to first post, changed the method name to go to first posts. Let's create that method. I'll just duplicate this one. Change the name to go to first post, remove the code inside that method to send the user to the post detail page for the posts with an ID of one, we can just do router dot push and then an object, set the name to post detail. And then we can add a params object and set the ID to save that. I'm currently on the post detail page of the posts with an ID of three. So let's click this, go to first post button and we're now on the post detail page for the posts with an ID of one. 32. Lists (v-for) [Module 9]: Lists using the V4 directive work exactly the same way in the composition API as they did before. But just to quickly demonstrate that, let's output this list of posts dynamically by using a data wrath and AV for directed. So let's jump to our posts page. So source views, posts view, dot view. And let's set up a href where we can place an array of posts. So I'm going to add a new comment here, which just says, we'll set up a constant called posts, will set that equal to a href. We need to import the ref method from view. I'll put this at the top here. Import from view will place an array inside this href. And each item in the array will be an object with a couple of properties. So we'll add an ID property and set this first one to add a title property and set this to post one. And then a lot of comma after this object, duplicate this twice. Just changed the IDs to two on the title to post two for the second I M. And then for the third item, I'll set the ID to ID3, set the title to post three, and save that. This posts array is now available in our template. So let's use it to spit out these allies with our router links dynamically using the V fall directive, I'm gonna get rid of all of the Allies and the routes are linked. So apart from the first one, save that, let's add a VFR directive to this LI. So v4 posting posts. This is going to loop through our posts array ref. As it's looping through, each object will be available at the placeholder that was specifying here, post. And we also need to add a key property, colon k equals. And we want to set this to something that's going to be unique. So we'll set this to postdoc tidy. Save that. And we can now see three posts being spit out. So let's output the post title here, which is going to be a postdoc title. And then let us update the link and the two prop as well. So I'll use a template string by adding some back ticks around this, get rid of ID1 and spit this out dynamically by adding dollar curly braces. We also need to add a colon to the start of this prop. And then inside the curly braces will output the ID which is at postdoc tidy. Save that, reload. If we click on post-war Rome, the ID one-page, free, click on Post to run the ID two-page. You can say there are absolutely no differences when it comes to lists under VFR directive in the composition API. 33. Template Refs: Let's talk about template refs. Now, I don't mean the data refs that we've been using for some of our reactive data. I mean, how back in the Options API we could add a href attribute to an element, give it a name. And then we could access this element on the component is mounted and then do something to it so it has focus it or out of class or figure out the elements width or something like that. I'll just remove this. Now, let's say we want to access this heading element is H2. We want to figure out the width of this element. We might need the width of this element for responsive design purposes or something like that. For example, we might need to change the size of the text, also thin if the element is too wide or not wide enough. In the composition API, we still are the href attribute to the element that we want to target. And you can use any name you like. But the accepted standard is to use camelCase with the word ref at the end. We might set this up, title ref, save up. How can we access this element when this component is loaded? Let's scroll down to our app title here. Now in the Options API, we could just do this dot dollar refs, dot a title ref. And we could then access the element and then do something with it, figure out its width or whatever. However, in the composition API, we don't have access to this dot dollar Fs, so this is not going to work. What we need to do instead is setup a const with the same name as our ref, title ref. So const title href equals. And then we need to set this to a little bit confusingly, a data href. We set this to a ref method and we set the initial value to know. And again, we're gonna need to import this ref method from view. So let's add it to our import here, rough comma, and we can now access this element by this href. Value of this constant went out component is mounted in this mountain, took what we can do is outside, so ref, dot value, we do still need to use dot value for template refs. This should give us access to this h2 element. Let's just lock this out for now. Console dot log. Save that. And we could say heading being locked out in the console. If we wanted to get the width of that element, we could just start dot offset width to the end of this. I'm just going to log out a template string with the text. The title is dollar curly braces, and then I'll paste in that code again outside. So ref dot value, dot offset width. And then after that I will just add pixels wide and save that reload. And we can see here it says the outside circle is 318 pixels wide for a free resize our AP reload. And now it says the title is 453 pixels wide. 34. nextTick: Next tick allows us to wait until the DOM has updated and then do something. For example, if we click on the Plus button here, our counter is updated, on the DOM is updated. Our next tick allows us to wait until lock dom update has completed before we do something else. Let's say when we increase our counter, we want to wait until the dom update has completed. Before doing something else, I'm going to jump down to our increased counter method, which is here in the Options API. We could do this dot dollar next tick and then pass a callback into that like this, and then do something after the DOM has updated. However, this won't work in the composition API. If I save that and change the counter will see an error in the composition API. We need to import the next tick method from view. So I'll scroll up to our import statement and just start next tick to the end. Instead of this dot dollar Alexis, we can just do next tick. Then pass a callback into that. Then we can do something when the DOM has updated. So do something when counter updated in the dot, save that. And if we change the counter, we can see that being locked out or since next tick is an async function, we can also use async await to do this. To do this, we do need to make our increased counter an async function like this by adding the async keyword here. Instead of this method with the callback, I'll just get rid of that. We can now just do await. Next tick. Lie that if we save that, increase the counter, we can say that that's still work in. 35. Teleport - Part 1: V3 brings us a new feature called teleport. Now this isn't specifically a composition API feature. We can actually use it with both the composition API and the options API in exactly the same way within any view three up, however, I wanted to cover it anyway, since it's such an amazing feature, allows us to move an element from its default place in the dump to somewhere else in the dog, usually outside of our view up as in outside of this div with an id of app, which is the root element of our view up or teleport it to another div, which is a child of the body. This is really handy for things like modals, which might not display correctly if we display them somewhere deeply nested within our apps DOM tree. Now this homepage is pretty complicated now and so is the posts page. So let's create a new page where we can play around with teleport. So I'm going to create a new view in source and views called modals view top view, template tag within a lot of div with a class of modals. And then I'll just start a H1 tag with the text modals. On Save that we need to setup a new route for this. So we want to go to source and router index.js. Let's just duplicate this posts route. Then I'll change the path to slash modals, change the name to modals, change the path to the component to modals view, top view. And let's save that and so that we can easily get to this page. Let's add a link to our navigation, which is in source app.vue. And let's add a new router link after this home link. So I'll duplicate that. Set the path to slash modals, set the text to modals as well. Save that. And let's see if we can get there. And yet we're now on these modals page. So let's jump to that modals view dot view. Let's create a button which when we click it will show a modal. So after this heading, a lot of button with the text show modal. Save that. And let's add some markup for a modal with some text on a button foreclosing it. So a lot of div with a class of modal inside that, a lot of H1 heading with the text, this is modal. And then a lot of paragraph with some Lorem Ipsum in VS Code, we can output some Lorem Ipsum by just typing in lorem and hitting Enter on this Emmet abbreviation. And I'm a lot of button underneath for hiding the modal. And I'll just put the text Hide modal and save that. And let's add some styles to give this a background color and some padding. So a lot of style tag. Target the modal class. Set the background to beige, set the padding to ten pixels. Now let's set to pay rent for determining whether or not this model is shown. A lot of our script setup tags, lot of comment, which just says modals, lot of constant called show modal and set that equal to a ref with an initial value of false. So if this is false, then we won't show the modal. And if it's true, then we will show the modal and we need to import ref from view. I'll add another comment here, which just says inputs. Then we'll import from view. And that will allow the v-if directive to this div with the class of modal. I'll split the attributes on that with the split HTML attributes extension which we installed earlier on. V-if show modal, save that and we can see the modal disappear. And if we change this value to true, let me see it appear again. Let's set it back to false. Let's change the value of this without buttons. On this show modal button, we'll add a click handler, which sets show modal to true. And that's working. And then on this high modal button, we'll add another click handler, which sets show modal to false, save that, see if that's working and that's working. We can now show and hide this modal. 36. Teleport - Part 2: Now let's add some styles to this model to make it absolutely positioned on full screen. So I'm going to drop down to the style section, set the position to absolute, set the left to 0, the top to 0, set the width to 100% and the height to 100%. Save that. You can say this hasn't worked. I'll just stretch this out a bit. Now the reason it's not worked is because this modal element has an ancestor element which has its position set to relative. We go open the dump and choose our root element, this div with an id of app. Look out the styles we can see this has a position of relative. This is exactly the reason why we often need to use the teleport component. And you'll see if we drag this div to be a direct child of the body, then the modal is full-screen, although it doesn't look quite right because I think we need to add a Z index to make sure it sits on top of everything. So I'll just jump back to the style and our desired index of one. Save that, reload the page, show the modal and I will drag this to the root of our page. As a child of the body. He say this now looks how we want it to. We can now use the teleport component to teleport this div with the class of modal to further up DOM tree, such as we've done here in the dev tools. To do this, all we need to do, I'll just reload that is surround the element that we want to teleport, which is this div. In a teleport component. I'll just select the div and move it into the teleport. And then we add a to attribute which we set to a CSS selector to determine where we want to tell apart this element two. If we just want to make this a direct child of the body, then we can just set this to body. I'll save that. And if we reload, I've spelled that wrong there that should be teleport, Say that again and reload. And you'll see if we show the modal now, it now looks how we want it to, because the teleport component has teleported it to be a direct child of the body. Or if we don't want to tell apart this to the body and we want to send it to a particular div, and we can do that as well. Let's just jump to our HTML page in the root of our project, index.html. Lot of div after this div with an ID of app, which is where our view up is living there for the class of modals dash container. Save that. And now if we jump back to your modals view dot view, we can jump to this teleport component and just change the two prop, two dot modals container and save that reload. Show the modal. It is moving the modal to the modals container div that we just started. However, it doesn't look right because all elements in this app are being given a position of relative because of this asterisk style here, we might just need to override this style for this modals container div. We could do this in our base CSS. If we go to source assets and base CSS, we can just jump down to the bottom. Target are modals, container. Container. Set the position to initial. Save that. Now it looks okay. 37. Child Components [Module 10]: Let's go over a child components in this module. Since a lot has changed regarding child components when we use the Composition API, especially how we handle props emits the new model value and update modal value pattern dynamic components provide inject. Let's start by turning this modal into a child component. I'm going to create a new file in the source components folder. For some reason I don't have that folder, so I'll just create it components. In there. I'll create a new file called modal dot view. A lot the template tags save that. And now let's jump to our modals view in source views and modals view dot view. Let's cut out our modal code including the teleport component, and then paste that into our template on modal dot view. For now I'll just remove the v-if directive. This click handler on the high modal button, since we don't have this data right here. I'll also put the styles from modals bu.edu. I'll put the entire style tag, paste that into modal dot Vue. Save that. Now let's import this model.py two-component into modals view dot view. Let's just remind ourselves how we did this with the options API. Allowed some more script tags are the export default. In the Options API, we first had to import our component input modal from slash components slash modal dot view. And then in our default export, we had to add a components, objects stick out component in there. We would then be able to use this component in our template. This should still work because remember, we can combine the options API with the composition API. Although I wouldn't recommend that if we stick this component on the page modal and put that v-if bucket. So VF show modal. Save that. Click on the button. We can see our modal in the Options API, this components objects was always a bit of a pain in the book because every time we add a new child component or rename a component, we always have to keep this components objects updated or in the composition API. We don't need to worry about that. All we need to do is import our component and we can use it. I'll just comment out this code. Let's do this the composition API way. And all we need to do is just import this component. I will just copy this import statement, will paste it here, and that's all we need to do. So I'll save that reload. We can now see our modal and high modal built-in isn't working, but don't worry, we'll fix that later on. 38. Fix Lazy-Loading Views: Before we move on, I've noticed that there's a very slight delay. The first time we load the modals page on the posts page, I don't always see it, but sometimes I do. It's a little bit annoying. So I'm just going to fix that. I'm going to jump to source and router index.js. And the reason this is happening is because we're lazy loading these components. In other words, when we load a component like this instead of important gate beforehand, I'm just adding it like this. Then the component only gets loaded into memory at the point when we visit that route, which does result in a very slight delay when we load those components for the first time. To fix this, we can just explicitly import all of these components beforehand, just like we've done with the home view dot view. So I'm just going to duplicate this line and put on view three times. And then I'll do a multiple selection on home view. I replace that with modals view. And then on the next one, I'll do a multiple selection and replace it with posts. For the last one, I'll replace that with posts detail view. Then I'm just gonna do a multiple selection on these three names by holding down the Alt key. And then I'm going to do multiple selection down here on each of these methods which are lazy loading those components, just paste in those component names. I'll save that. We should now no longer see any delay and all of our pages should load nice and quickly. Even the first time we hit them. 39. Slots: For the most part, slops, work in exactly the same way in the composition API. Just to demonstrate that if we jump to modal dot view, instead of hard-coding this content into this modal w component. Let's pass this down from the parent component models view dot view using a slot. So I'm going to jump to modal dot view. Cut this paragraph and instead just place a slot here and save that. And then in modals view dot Vue will paste the paragraph in here which I cooked before. Let's save that jukebox who are modals page click on the button. And yet that content is getting through using our slot and also named slots work in the same way as well. I'm going to jump to modal view. And let's say we want to pass the modal title down using a named slot. I'll just remove this text and add another slot here. We'll give this one a name of title. Save that. We should now be able to pass this title down using a named slot. So to do that, we can just add a template tag here typing the concept we want to send using this slot, so my new title and then to our opening template tag, we need to add v dash slot, colon, and then the name of US law, and we named it the title. Title. Let's see if that's working. Yeah, We can see my new title there. And we can also shorthand this v dash slot colon bit by using a hash symbol instead. So I'll say that see if that's still working on yet. I still work in. You can see this all works in exactly the same way as before with the options API, the only thing that works differently is how we programmatically access our slots data. Now if we want to access our slots data in the template, we can still do that in the same way as before. So after this slot here, I'm just going to add a pre tag. We can still access our slots data using the dollar slots keyword, just like we could in the Options API. So if I want to drop the data related to The title slot, I can just do dollar slots dot title, and then parentheses. Save that. And we can see all of the data related to our slot being spit out. However, if we want to access this data programmatically in our script section, which is probably something that you'll rarely need to do. Although I did find the I needed to do this once in my budget to that I'm working on them. We need to do this a little bit differently in the composition API. So a lot of our script setup tags. So in the options API, if we wanted to access this slots data, we could just do this dot dollar slots dot title. In the composition API, we need to import our composable from view, which is the US slops composable. What we can do instead is import, use slots from view. And then we can assign this composable to a constant. So const slots equals use slots parentheses, and we can then access our slots data using this slots constant. If we wanted to get this title again, we could just do slots dot title, parentheses. And let's just log that out. Console.log. Save that, show the modal and we can now say our slots data being spit out in the console. Again, this is not something you're going to need very often, but there are some rare cases when you will. Before we move on, let's just remove everything in the script section here. And let's remove this pre tag that we've added as well. 40. Props: Let's look at how we handle props using the composition API. Now the way that we pass props down from apparel component to a child component is exactly the same as before. It's only the way that we receive props in the child component from the parent component that's changed. So let's jump to modals view, dot view. And let's say we want to pass this title down using a prop instead of using a named slot. So let's just remove this template tag here. I'll split the attributes on this modal, and instead we'll pass this down using a prop, so we can add a prop called title and then pass in our title. So my modal title parentheses via prop. Save that. Now let's jump to modal dot view, and now we need to receive this prop in the Options API. I'll just start another script section, the export default. The options API. We added a props option. We can either set this to an array or an object. We could place our props in here. We could add a title prop, settler a2, an object. And we could set the type to string, because this is going to be a string. If we want, we can set a default value in case the prop has not been specified on the parent component. So we could set the default to know title specified. Save that this should still work as remember, we can combine the options API width, the composition API. So I'll get rid of this slot here. And instead just output. Look curly braces and the title. Save that. And let's see if we can see the title in our modal. Yet we can see the title that my modal title via prop. However, we do this a bit differently using the composition API. Let's comment out this script section. We'll do this the composition API way. So first I'm just going to add one of these block comments, which says prompts. To receive our props using the composition API, we need to create a constant called props and set that equal to a new method called define prompts. One thing to note is that unlike most of the V methods such as Raf and reactive, and computed, etc, we don't actually need to import this defined props method from view. It's just automatically available. And now we can just define our props in the same way within these parentheses. We can pass these in as an array like this. Or if you want to get more specific and pass in the type and the default, etcetera of our props, then we can pass in an object. Instead. We can define our props in the same way. I'll just copy this title prop that we define before. Paste that in here and save that. And hopefully that should be working. Now, I'll just reload the page, show the modal. And yet we can see the prop is getting through. And let's just make sure our default value is getting through if we don't specify the parent component. So I'll jump to modals view dot Vue. Just removed this title prop and save it. And yeah, we can say no title specified and I'll just undo that and put it back in and save it. One thing to note is that when we use a props in our template, we can either just use the straight oak name of the prop. We have here title, and we can also use props dot title, save up. You can see that these both work. I'll just put this back to just title. However, if we want to access our prop in our script section, and then we do need to use this props constant. So if I try to console log title, then we see an error. However, if we console log props dot title, then we can see the prop being locked out. Before we move on, let's just remove this console log and save that. 41. Emits: We can't currently close this modal. So how can we get this button when we click it to close this modal? Well, this button is on modal dot view. And if we jump to its parent component modals view dot view, we have this show modal ref. This is determining whether the model is shown on up. So when we click this button on the child component, we need to somehow set this modal href, which is on the parallel component, back to false in order to hide the modal. And one way we could do this is by emitting a custom event. Let me click on this button and then listening out for that event on the parent component, on this modal component. And then when we receive that event, we can just set show modal parts of false, those hiding the modal. So let's jump to modal dot view and will emit a custom event when we click this button. Now in the Options API, I'll just uncomment this code. For a sec. We had to add a emit option set to an array. And we had to declare our custom events here, such as hide modal. And we wouldn't be able to custom event from our template, but we declare our limits differently with decomposition API. So I'll just comment this out again. What we need to do, I'll just start another block comment here, which says emits. What we need to do in the composition API to declare our limits is create a constant called emit. Set that equal to a method called define. Emits. A bit like the defined method. Again, likely to find props method, we don't need to import this method from view. And then we just pass an array into that where we can declare all of our custom events. Let's declare and emit called Hyde modal. We can now emit event hide modal from anywhere in our template. We can now just jump to this button element and add a click handler and then use the dollar emit method and then just omit this custom method hide modal. You can see the way that we use emits in our template is exactly the same way as before. It's just the way that we declare them in our script section that's different. If I save that, we can now listen out for this event on the parent component modals view, dot view. So let's jump to that. If we jump to this modal component, we can just thought I'd hide modal, which is the name that we used here and here. And now we can do something when we receive that event and we just want to set show modal back to false. We can just do show modal equals false. So let's save that and see if it's working. I'll reload the page, click on the button, and then click on the height button. We can say that's now working. This is how we emit an event directly from our template. But what if we need to emit an event programmatically from our scripts section, well, with the options API, we can just do this dot dollar emit and then emit the event like this. However, that's not going to work in the composition API. Instead, we need to use this emit constant. But before we do that, when we click this button, instead of emitting the event directly from the template, let's trigger a local method instead. Let's trigger a method called handle Wilson click. Then I'll add another block comment here, handle button click sets up that method. Let's just make sure that's triggering. When we click the button. That's triggering and salt to admit our event from here, all we need to do is emit and then the name of our custom event. So hide modal. Let's save that and this should be working now. So I'll reload, show the Modal onclick. On high modal, we see the modal disappear. 42. modelValue: Right now when we click on this hide modal bolted, we trigger this handled button click method. And then in there we emit this custom event hi modal. Then on the parent component we listen out for that customer event and then set show modal back to false. But we can actually simplify this by accessing this show modal ref directly from the child component modal dot view and changing it directly back to false. Those removing the need to setup custom events and then emitting those events in the Options API, we could do something like this with the sink modifier. But in the composition API, we can do this with V model, the new model value prop, and the update model value events. First of all, let's jump the modals bu.edu. Instead of this v-if directive will add av model directive, and we'll set that to this href show modal so that we can get this child component access to this show modal ref, oops, I've typed in V modal that I should be V model. I'll save that. Now if we jump to model.fit, we can get access to the data property that's being passed in with V-model using a special new prop called model value. And I usually declared this prop up the top of my drops objects. So a lot of that here. So we just modeled volume. We can set the type if we like. This is gonna be a Boolean because I'll show modal ref is a Boolean. So we'll set the type 2 billion, will set the default to false. Let's save that. Now let's use this modal value prop to determine whether the modal is shown. Instead of the v-if that we had before, the parent component. We can just jump to this div with a class of modal out of v-if directive and set that to vf model value. In other words, if this show modal ref is false, then it's gonna get passed down with the model as false and then received as modal value. And so modal value will be false on this div with a class of modal will not be shown. However, if show modal is trig, that's gonna get passed down with V bottle and then picked up by this model value prop, where it will still be true. And then we'll show the modal. So let's just save that and see if it's working. I'll click on the button yet we can see the modal. We know this is getting through correctly. 43. update:modelValue: In order to hide this modal, was still triggering this handle button click method. When we click the button, then we're emitting this costal events high modal and I'm listening out for that event and then setting show modal back to false. But we can actually manipulate the href that we're passing in here show modal directly by using the new update model value event. This way we can get rid of this high modal event and remove it from this component. So let's jump back to modal dot view. Instead of emitting this custom event, emit a new event called update colon model volume that we need to pass the value that we want to change the modal value two. So we want to change it to false. What this is going to do is whatever value we are passing in with V-model is going to change it directly to whatever value we specify here. If I save that, now, show the modal, I click the high modal button. We can see the modal is hidden. Now we do see a warning in the console here, event update model value, but it is neither declared in the emits option nor as an update modal value problem. We just need to add this event to our emits array here, like this, updates colon modal value. This should get rid of that warning. Show the modal and hide the modal. And we no longer see the warning. Since we're no longer using this high modal event anymore, we can just remove it from this array. Save that. And if I jump back to modals view, dot view, we no longer need to listen out for this hide modal event since we are now manipulating the show modal vector using the update modal value of n. So we can just get rid of this now. Save that. Now let's see if this is the work. And yet it's still working. Let's jump back to modal dot view. We can also emit if I had update model value directly from our template as well. So instead of firing this method here, I'll just comment out this method, but leave it there for reference. We can just change this click handler on use the dollar emit method. Just simply emits updates colon model value and then set the value we want to set it to, which is false. Save that. And let's see if that's still working on yet, that still work it as well. So you can see that using modal value and update model value massively simplifies our child to parent communication and often removes the need to setup custom events and then listen out for those custom events. 44. Dynamic Components - Part 1: Dynamic components allow us to switch out the component that is being used in a particular parts of our app. Let's demonstrate this by creating a dark version of our model, which we can switch out on this modals bu.edu page based on a checkbox. Now in a real-world app, if you wanted to have modals which were both light and dark, then the best thing to do would be to just have one modal component and switch out the styles using props. What we're about to do is not exactly a best practice, but it's a nice, easy way to demonstrate the concepts of dynamic components. First, let's make our modal component on modals view dot view, a dynamic component. So all we need to do is change this to a component. Component. I'll make sure both the opening and closing tags have been renamed. And then we just add an S prop. So colon is, and we just set that to the component that we want to display here. We just want to display our modal component. So we can just use the name modal. Set this to modal save that. It should be working just like before. I'll click on show modal. And yet we can still see the modal. We can still hide it, except now we can actually change the component that gets displayed here by simply changing this is prop, programmatically. So before we create our dark vision of our modal, let's just add a checkbox to the page which we can use it to toggle whether or not we show light or dark modals. So after our heading, I'm going to add a div. And then inside that I'm going to add a label. And then inside that I'm going to put the texts show Dark modals, question mark. And then after that texts allowed an input, set the type to checkbox. Let's save that to make sure it's looking okay. And yeah, that looks pretty decent. Now let's set so pay ref, which we combined to this checkbox. So let's scroll down to our script section on above this show modal href. Let's add a new ref called Chau Doc modals that equal to a rap with an initial value of false. And we'll use this graph to determine whether we show dot modals or light modals. So now let's find this href to our checkbox, which is here. We can just set the V-model to show doc modals. Save that. Just to make sure this is working, I'm going to add a pre tag after this div and just output show doc modals. Save that we can say is false initially. And if we check, the checkbox is set to true. And if we uncheck, it is set back to false before we create our dot modal, let's just remove this pre tag on Save. 45. Dynamic Components - Part 2: Let's create our dark modal. So let's jump to modal dot view. I'm just going to remove these comments on this one as well. Save that. And now let's duplicate this component module to source components on modal w. Duplicate with the duplicate action extension will name this modal doc dot view. Let's change the class on this div to modal dash dark. Scroll down to the styles, changed the class here to modal dash dark instead of a beige background. We'll set this to hash 333, which will be a dark gray color. And then we'll set the color, the text color to white and save that. Just to reiterate, if you wanted to have modals which were light and dark in a real-world up, then this wouldn't be the best way to do it because we're duplicating a lot of our code here. It'd be better to just have a single modal dot Vue component, which we can then pass a prop to say a prop called dark. And then if we receive that prop, then add a different class to the modal to give it different styles. I'm just using this as an example to demonstrate dynamic components, but not as an example of best practices. Now let's jump modals view, dot view, and we can now switch out the component that gets used in this S prop based on our show dot modals ref. So first of all, we need to import the dark modal component. I'll duplicate this line, change that to modal doc, change the filename here to modal dark. What we can do in this is prop. We can use a ternary if statement here to spit out a different component based on our show dot modals ref. So we can do if Chau Doc modals is true, then we'll use the modal dark component. Otherwise, we'll use the modal component. So let's save that. Click on the Show modal button. We see the light modal high Doc. I'll check the checkbox which showed set showed up modals to true. And we should see our modal. Now, if we click on the button, there we go. Now we can easily switch back and forth between our light and dark modals. 46. Provide / Inject - Part 1: We saw how to pass data from a parent component to its direct child component using props. But what if we want to pass data down to a really deeply nested child component, well, using props, we would need to pass the data from parent to child to child to child and so on until we reach the desired child component on this can be a very messy way to pass data around. And we can get around this problem by using provide inject. Let's jump to our root component App.vue. Just going to close everything first and we'll go to source and app.vue. Let's say we have a reactive object on this component with some data which we want to pass all the way down to this modal component. First of all, let's just set up a reactive object on app.vue. Let's add our script section, our script setup tags. Let's say we have a reactive object with some user data. A lot of block comment here, which says user data, sets up a constant called user data set that equal to a reactive objects. Now we need to import reactive from view. A lot of other comment which says imports will import reactive from view. And let's add some data to this user data reactive objects. Let's say it has a name property. We'll set that to Danny and a username property, and we'll set that to Dani caudal. Save that. And let's just display this data in the top right-hand corner of our page. I'm going to jump up to the top of the template. Add a div with a class of user data that allow double curly braces. User data dot name. Then we'll output the username as well. So a lot an at symbol and then double curly braces, then use a data dot username. Save that. Let's just thought some styles to make this look a bit better. I'll scroll down to the bottom of the style section. Target user data div. We'll set the position absolute, the background, beige at the top to 0 to stick it on the top, the right to 0 as well. So we've got it in the top-right corner. I'll set the font size to 12 pixels, a little bit plotting as well, five pixels. Save that. Now let's say we want to pass this data down to modal component using props. Well, first of all, we need to pass it as a prop to our router view component, like this, colon user data equals user data. Save that, that we need to receive this in our modals Page. Source Views, modals viewed up view. I'll just add a comment here which says props. That we need to add a constant named props, set that equal to define. Props pass in the user data. Prop. I'll set the type to object. And now we need to pass this user data objects again down to the modal component, which is, Hey, we can do, go along user data equals user data, save that. And now we need to open up the modal component received this prop. We need to add the prop here. So use a data set, the type two objects. We can now finally use this user data objects on our modal. So a lot of div after this button here. There I'll just put username is double curly braces. Use a data dot username. Save that, reload, show the modal. We can see the username in the modal. You can see that this gets messy very quickly, especially on a real-world up when you might have components which are much more deeply nested. Done this modal component. 47. Provide / Inject - Part 2: So we saw that I didn't want to pass data from app.vue down to modal dot view, we have to pass it as a prop to our router view. Then receive that prop in modals view, dot view, and then pass it down as a propaganda to model.py EU and then receive it in modal w before we can actually use it. This approach isn't much trouble. So let's fix this problem using provide inject. So first let's just remove all the changes we made here with props in model.py view. Let's remove this div that we added and also remove this user data prop from our props object. Save that, and then jump to modals view dot view. Remove this a user data prop from the component, component, and then remove our props object. Save that. Then we'll jump to app.vue and remove this prop from our router view, save that reload. And now let's use provide to make this user data reactive object available to all of our app.vue child components, no matter how deeply nested they are. First, we need to import the provide method from view. And now after we declared this user data reactive objects, we can just fire this provide method. For the first parameter, we just need to provide a placeholder name for this data so we can access it in our child components. And we do this as a string. And I'll just give this the same name as our reactive object user data and then follow the second parameter. We just pass the data that we want to make available, which in this case is this userData reactive object. I'll set the second parameter to use a data. Save that. And now this data should now be available to all of app.use child components. We now don't need to do anything to model's view dot view. We can just skip right down to the grandchild component model.py view and we can grab this data using inject. Let's jump straight down to the grandchild modal dot view. And now we need to import the inject method from view. Loud comment which says impulse at the top, will just import inject from view. And we can now use this method. So I'll jump down to the bottom of the script section out a new comment which says user data. All we need to do to grab this data that's being provided is phi of the object method. And pass in the placeholder name as a string, which we used in our Provide method, which is user data. All it needs to do is inject user data to make it available in this template, we just need to assign it to a constant or a variable. So we'll just assign this to a constant with the same name, user data like that. And we can now access this data with this userData constant. So if we jump back up to the template again, a lot of div after this button. And I'll just put username is colon, double curly braces, and then use a data dot username. Save that, reload, show the modal and we can now see our username. But we've massively simplified our components by using provide inject. 48. What is a Composable? [Module 11]: In this module, we're going to learn about composable. On composable are one of the most exciting features to come with FY2013 on the composition API. But what is a composable? Well, you can think of composable as the composition APIs equivalent of mixins from the options API. Although with a lot of added benefits, they allow us to extract out reactive data and methods. I keep them separate from any particular component so that we can easily reuse those reactive data and methods across multiple components without having to duplicate code. We have an example here of some code from a view component using the composition API. And we have some code related to a username. Here, we have a username data ref for storing a username, and then we have a function for updating that username. So let's say we want to reuse this username rref and this update Username method on multiple components. That we can just extract these out into a composable, like this. Composable is basically just a function and we can paste all of our reactive data and methods, watches, computed properties, etc, into this function and then return the stuff from this composable that we want to make available. We can then import this composable into any component and then use the structuring to extract the things that we want. In this case the username data ref and the username function. And we can then use these in that component. If you want to learn more detail about the benefits of composable over mix-ins, then you can jump to this article on View skill.io. What is a Vue.js composable? A lot the link to the lecture. But in brief, the main benefits of composable over mixins is first of all, they make it completely clear where all of our data and methods, etc, are coming from. If we look at this example from the options API here, we have a component which is importing three different mixins. And then we're using a data property called site from one of these mixins. But from looking at this code is not obvious where this site property is coming from without having to open up these mixins and look through the files. Whereas with composable, we always know exactly where our data is coming from. You can say in this composition API example, we're importing three different composable. Here. I'm grabbing a name property from one of them. We can clearly say that this is coming from the US product composable. So composable is make it much clearer out where everything is coming from. Composable is also reduced the risk of naming collisions. So again, we have an Options API example here which is pulling in three different mix-ins. Now if all of these mix-ins have a data property called name, and then we use that in our component. That is only going to grab the name property from the last mixin, which we've imported. The name properties in the other two Mixins will be completely ignored. Composable is also make it easy for us to safeguard our reactive data. In other words, make the data properties in our data objects read-only from outside of the mixing, so that we can only actually change the data within the mixin. We can't do that with mix-ins. What we've composable. When we return the stuff that we want to expose from the composable, we can expose values as read-only so that they can't be changed from outside of the composable, only from a method which is inside the composable. The last benefit of composable is that we can actually use them as a global state management system. So we can effectively create a composable store with a bunch of different data and methods. When we change the data properties in that store. They will always be up-to-date on all of the different components in our app. Whereas we can't do that with mix-ins. If we tried to do that with mix-ins, then the data will always be reset for every component in which we use that mixing. Whereas with composable, we can easily create a state management store where when the data changes, we will always see the changes to that data reflected in all of the components which are using that composable. If you don't know what state management is and don't worry because we're gonna be covering that in the next module. Well, these are some of the main benefits of composable over mixins. 49. Create a Composable: Let's create our first composable. Let's jump back to our homepage. We'll open up the homepage. So source views and whole view, dot view. Let's say we want to use our counselor data and the related methods on multiple components. Well, we could do this using a composable. Now let's jump back to our explorer. And it's recommended that all of our composable go into a folder called use. In our source folder, we'll create a folder called US. And in there we'll create our first composable. And by the way is recommended that all filenames for composable are written in CamelCase. Begin with the word use. We'll call this composable. Use counter dot js. In our composable, we just want to create a function with the same name as our file use counselor. Then we want to export this function to make it available to all the files. And now we can just cut all of our accounts of data and related methods and paste it into here. I'll jump back to our home view dot view. And I'll copy all of the code related to counter, the counter data reactive objects, the watcher, the odd or even computed property, the increased counter and decrease calcium methods and the unmelted hawk. So we can just cut that joke to use council dot js inside this function. And we do still need to import all of the few methods that we're using such as reactive and watch and computed. So I'll jump back to a home view dot Vue. Just copy the import statement. Jump back to use counter dot js. Just paste this to the top. I'll just stretch this out a bit. We're not using the ref methods, so we can get rid of that and save that now to make these data and methods available to any components in which we use this composable, we need to expose them by returning them at the bottom of this function. Just like we did in our components when we were using the older setup function pattern for our components. So let's start a return object here, and we'll just return the stuff that we want to use. Let's return the counter data, reactive objects, the odd or even computed property, the two methods for increasing and decreasing the counter. So we can just return counter data, then odd or even, then increase counter and decrease counter and save that. And we can now pull this composable into any component we want and get access to the data computed property and methods that we're returning here. 50. Use Our Composable: We can now import this composable into our homepage which is now broken cause it's trying to access counselor data. Audit an even computed property which no longer exists in this component. So first of all, we need to import this composable. So we can just do import. Then the name of the function that we created here use Kaltura import, use counter from slash, users slash use counter, save that. By the way, there's some stuff that we're no longer using in this import statement. We can see that it's grayed out. So I'll get rid of reactive computed and watch next tick. And now we need to get access to use council composable. So let's scroll down to this counter comment which are left in before. There are two ways we can access the data and methods that are in our composable. If we don't want to change our template, which is currently looking for an object called Kaltura data methods called decrease counter and increase counter, this auto or even computed property. Then we can use the structuring to grab only the things that we need from our composable the counter data odd or even under our increased counter and decrease counselor methods. To do that, we can create a constant which is an object and set that equal to our US counter composable. And then we can just add the things that we want to extract from the decomposable into this object. So we won't counselor data odd or even, increase counter, decrease counter. Because these are the same names that we're using in our template here. Caltech data decrease counselor increase counter, odd or even than this should not work. So I'll save that reload the homepage, which now seems to be working again. We can say everything is still working, included our computed property here. Now the other way that we can access all the stuff in impossible is by just assigning the whole of our composable this use COUNTA function to a constant or variable like this. For example, we can then access our counts, a data object, counter data dot counter objects. And we can access our increased calcium method or counter dot increase counter. If we're doing it this way, we'll need to update our template, are prepared and everything with counselor dots where we're using Counter data dot title, we need to prepend that with counter dot. All of these methods we need to prepend with cancer dot and then we need to prepare this counter data with counts adopt and our computed property with counselor dot on the V model on this title here with counselor dot as well. Save that reload and hopefully everything is still working. Yeah, it seems to be working. However, although sometimes grabbing the whole composable like this might make sense in this case, and in many cases I think the previous approach makes more sense because it allows us to see exactly what we're grabbing from this particular composable. So I'm just going to undo everything. I just did. Get rid of all those counts of dots that we added. Change this back to the D structuring approach where we can clearly say exactly what we're grabbing from the composable. Just make sure that's still working. Everything is still working. 51. Reuse our Composable: Now let's reuse our composable on our posts page. So let's open that up. Views and posts view, top view. Let's say we want a much simpler version of our counter on this page, let's say we could add a button which has the counter inside it. And then when we click the button, it increases the counter by one. So first let's just add the mockup. After the text area. I'll add a button. Inside that. I'll just put 0 for now. Lot of class of Counter button save that. Let's just surround that in a div so it goes onto a new line. Save that allowed some basic styles. So dark counts or the font size to 60 pixels, set the width to 100%, set the background color to pink. Save that. We want to replace this 0 with our counter. And when we click the bottom, we want to increase the counter. So first let's import the composable. So import use counter from slash users slash use counselor, save a lot of new comment down here. Counsel Bolton grew up the stuff that we need from the composable. So we'll set up a const with an object, set that equal to our composable function. You use counter parentheses. And all we're gonna need for now is the counselor data reactive object which contains our counter, the increase counter method. Save that. Now let's jump back up to our template. Get rid of this 0, replace it with double curly braces and counselor data, dot count, save that. And then let's add a click handler to this button. So click increase counter. I will increase it by one save up. You can say this is now working. Now let's say we decide that we want this button to be yellow. When the number is odd. We can do that without even computed property that's in our composable. So we just need to jump down to our D structured object here and also are the odd or even. And let's scroll back up to our button. So let's say we want to add a class to this. If the number is odd, we can just thought a class of yellow, let's say. But only when odd or even is equal to odd. Because remember this auto or even computed property returns a string of even or odd. I'll save that. And if we look at the button in the dev tools, we click it, it changes to one and we can see that class of yellow is being added. To click it again, the class has been removed. So let's just add some style to make this yellow will target the counter Dash button element again, but when it has a class of yellow, set the background color to yellow, save that reload. We get out, say that books, it is yellow when the number is is odd. Now one thing to note is that the council will be set back to 0 when we change pages. If I jump to the homepage, we can say this is 0 again. If I increase it and jump back to the posts page, this one is back to 0 again. That's because a new instance of this use counter method will be created every time we change page. If we want our counselor to be a global counter that won't reset when we change pages and we can just move this counselor data object outside of the US Council function. I'll just select this and move it outside of the function. And that will make this council data objects a global objects which is available everywhere. So we'll save that. And now, if I increase the character to, let's say six and we dropped to the homepage because say it's still six. If we increase it again here to let's say ten, joke back to the post page. We can see the counselor is ten and we now never lose track of our global counter. 52. Add Composable from VueUse: A great source of ready to use composable is the view use library. Let's jump over to view use.org. All we need to do is install this library and we'll have access to over 150 ready-made composable. So let's jump to get started. And here's how we can install a library with this command. So I'll copy that, open up our terminal. Alkyne is deaf process by pressing Control and C on that shortcut might be different on your platform. And then we'll launch this command that we just copied. Now installed. So let's relaunch our project by running npm run dev. We should now be able to use this library. I'll just stretch this out a bit. I'm going to jump to census. Scroll down. I'm going to use this use online composable. This composable allows us to easily see whether or not we're currently online. So if you look at this status online here, if I disconnect from the Internet, you can see that change to offline. And I'll just reconnect. I'll use this one because it's a nice and easy one to integrate. So we could use this in our little user info widget in the column. Let's close everything. Will open up source app.vue. Let's jump back to the documentation page. We just need to import use online from if you use slash core. So let's copy these two lines. Will paste them to the top of our scripts section in app.vue. Indent that. And I'm going to call this one, put it into its own section here. A lot of comment which says online status. Paste that in there. And now this online constant should change the True or false depending on whether we're online or offline. So let's use this in our little user widget. So after the username, a lot of pipe and a space, and I'll just hit Enter to stick this on new line. Loved the Word Online double curly braces. We should be able to output this online constant. Save that. Now we can say online true, and if I disconnect the Wi-Fi, you see online false. We could improve this by using this online constant to spit out different texts of different colors depending on whether this is true or false. So I'm gonna remove all this, just put the texts network status space and then a new line and that a lot of span. And then inside the double curly braces, I'll use a ternary if statement here. If online is true, then will spit out online. Otherwise will spit out offline. Save that. And it's set to online right now, let's disable the internet. We see a change to offline. Let's change the color as well. On this spot, I'm gonna bind to the style attribute. We'll set the color property, and again, we'll use a ternary if statement. So if online is true, then I will set the cooler degree. Otherwise we'll set it to red. Save that. And we can say network status online with green text, disabled the Wi-Fi. And we say offline with red text, and I'll just re-enable the Wi-Fi. Make sure you check out all of the different composable that are available in view use before you start working on your next big view project because it could save you a lot of work. 53. What is State Management? [Module 12]: In this module we're going to add state management to our art using penny on. But what is state management? Well, first of all, let's take a look at the problem that it solves. So I have three of our apps components, open hair, App.vue, which is our root view component. Then modals view dot view, which is this page, which is a direct child of App.vue. And then I have modelled up view, which is this modal, which is a direct child of modals view dot view. We saw earlier on that we could pass data from App.vue, this user data object, down to its grandchild component modal dot view using props. In order to do this, we have to pass a prop down to router view component. Then in modals view dot view, we had to receive this prop by setting up a defined prompts method. Then we add to pass this prop down again to our modal component. That in modal dot view we have to receive this again by adding it to our props object. And then we could finally use this data in modal dot view. And we saw that this approach was a real pain in the ball. We then improved upon this by using provide inject, which meant that in app.vue, we could just provide this user data object once those making it available to all of our app.vue descendant components, all of its children and grandchildren. And then in modal dot view, we could just inject this user data object once and then use it in our template. This was definitely an improvement. We still might have some problems here. Number one, provide will only make data available to descendants of the component where we use it. So it will only make the data available to children and grandchildren of that component. But what if we have a component which is not a descendant of App.vue, which is not part of its component tree. And we want that component to be able to access this data. In this case, provide is not going to work because they only makes data available to descendants of this component. The other problem is, what if we want to be able to change this data that's in app.vue from its grandchild component modal dot view. Well, right now we would need to trigger a custom event on modal dot view. Listen out for the event on modals view dot view, trigger another custom event. Listen out for that event on app.vue before we can finally trigger a method on App.vue, which then changes the data. And again, this gets very messy. If we want to change this data from a component which is outside of App.vue component tree, then we can't do that at all unless we're using an event most library or something like that. Even using event most libraries can get pretty messy to state management allows us to solve this problem by storing all of our apps core data, and all of the methods related to that data in one single centralized place, which is outside of all our components, but in a way that all of our components can access all of the data and methods. The place where we store these data and methods we call a store. Because when we created our projects, we chose to automatically add opinion to our app. The build tool has already created a penny or store file for us. Let's open that up. I'll open up the Explorer. And we want to go to Source stores and counts ab.js. In opinion as though we have three main sections. State actions on guesses. On state is where we store all of our data properties. So you can see a counter data property here. Don't be confused by the fact that we've created a counter up. This code is not been generated based on anything that we've been doing. This is just the default store that ships when we opinion to our projects using the build x2. We have state where we store all of our data properties. We're going to be adding our counter and counselor title data properties here. Then we have actions where we can store methods which can access the data that's in the state and also modify it. You can see we have an increments auction here, which is increasing these counselors in the State. And we'll be adding our increased counselor and decrease council methods here later on. Finally, we have guesses on here. We place methods which can grab something from the state, then possibly modify it in some way, and then return it. So you could think of these as kind of like computed properties. A state management store like this is available everywhere within our app. All of the components in our app, no matter which component tree that were part of or how deeply nested they can access all of the state actions and gets us that are in this store. This makes it much easier for us to manage our apps called data and all the methods related to that data. And it also means we don't need to worry about having to pass data from parents, a child to child using props or provide. We don't need to worry about bubbling up events from child to parent to parent, et cetera. Since all of our apps components have access to everything that's in this store. In a v3 out, there's a few different ways that we can integrate state management unless discourse space next. 54. Composable State vs Vuex vs Pinia: In a view three up, there are three main ways that we can integrate state management. Number one, we can integrate state management by using composable. Number two, we can use UX, which is a state management package created by the Vue team. On number three, we can use penny, which is another state management package created by the Vue team. Let's take a look at number one, state management using composable. Well, if we jump to the composable we created before, use counter in the US folder. What we've settled here is the basis for a global state management system. Because we have our counts are data hit, which you could think of a state. We have a couple of methods for changing the data in that state, which you could think of as actions. We have this computed property which grabs the data from the state and outputs a string based on that, either even or odd. We could think of this as a getter. Because data object is outside of this function, then this data is global. And so we can change this from anywhere and always see the changes that we make everywhere else. So we saw that we can increase the counselor on the homepage and then jump to the posts page. We still see the counselor is set to the same value that and likewise, if we jump back to the homepage. So we can actually use composable like this to manage all of the state management of our app without having to install any extra packages. If you want to learn more about state management with the composition API and checkout my composition API state management video on YouTube. Link to this lecture. However, if you want a more standardized and optimized approach to state management with more advanced debugging capabilities and DevTools integration. Then you'll want to use a package built specifically for state management, such as UX or penny on. For a long time, the UX has been the gold standard for state management for Vue apps. However, according to Avenue, the creator of you, the current gold standard for state management for V3 composition API Apps is Penny. Penny or has a few advantages over UX. If we jump back to this store, this penny a store counts ab.js. In opinion, though we only have three sections, state actions and getters, our actions can both access the state and manipulate it as well. While in view, actions cannot manipulate the state. They can access the state for the account, manipulate it. If this was a UX door, then this wouldn't work because in a store as well as state actions and gets us, we also have mutations, which are methods which can manipulate the data that's in the state. If this was a view next door and we wanted to increase this counter, we wouldn't be able to increase the counter from this action. We'd have to trigger a mutation. First. We might have a mutation called increments. This mutation would then increase the counter and we would need to trigger this mutation from the auction using something like Dispatch increments. This means in a view next door, you often end up with tons of different methods which have the same name, Where we are triggering an action and then triggering a mutation and then changing the state in a complicated store. This resulted in tons of extra lines of code and complexity to our store, which we don't need to worry about with pin yet. Because with Penny Are we don't have mutations. We only have actions. And actions can both access the state and manipulate it as well. We also have more advanced debugging capabilities built into Pena, which we'll see later on. It's also extremely light around one kilobytes and it's optimized specifically for the composition API. Let's see how we could use penny on throughout state management to this app. Since when we build our projects, we chose to automatically add Pena. Then opinion has already been added to our project and this store has already been settled for us. We can use this as a basis for our state management. However, later on in the course, when we create the note balls up, I'll be showing you how to add penny on, unsettled your first store completely from scratch. This is really handy to know in case you ever need to add opinion to a v3 projects which already exists. But if you're curious about how to do that now, but just jump forward to the note balls. Penny, a module, watch the first couple of videos and then jump back here. 55. Pinia - State - Part 1: We're currently using this use counter composable to manage all of the functionality of this counter. So we have all of our data and methods computed property in here. Let's instead use penny F2 sets up the state management for this counter up. First of all, let's remove this use council composable from this homepage and all of the dynamic functionality. Let's jump to home view dot view in the views folder will scroll up to our imports, just remove our use counter composable. And then let's scroll up to the top and remove all of the dynamic functionality. So this h3 where we're displaying our title, let's replace the double curly braces with hard-coded title. And let's remove all of these click handlers from our buttons where our counselor is being displayed in this span. Less replace that with just 0 and where it says discounts that is odd or even. Let us select the curly braces and we'll just pull odd slash even for now. Then this impulse, actually I'm just going to split the attributes on that. Let's just remove this V-model for now. I think that's everything. We have an error here. Use counselor is not defined. Let's scroll down to our script section. This error is because we're trying to pull stuff from our US counter composable which were no longer importing. Let's remove all of this as well. Save that, refresh. And we've now removed all dynamic functionality from this page. This is all just mark up now, oh, it looks like about pull some types of attacks there instead of the word title. So I'll just fix that and save it. Let's add some state properties for our counter and our counselor title. We'll jump to the penny a store file, which was created when we generate the project is already called counter. So we can just leave the name as it is. And again, this is just coincidental. This store wasn't generated based on any of our code. It was generated when we first created the project. For now, I'm just going to remove the auctions and gets us and just focus on state will remove this state property as well. Let's not a state property for our counter. I'll just call this count, set that to 0 by default. A lot of property for our counter title. And we'll just call this title. And we'll just set this to my counter title. Save that. We can now use these state properties anywhere within our app. Let's see if we can use them on this homepage. 56. Pinia - State - Part 2: To use our counter store, we need to import it and we importance it using this method name here, use counter store. So let's jump to home view dot view, where all of our calcium markup is. Scroll down to the impulse section. And we'll do import, use counter store. And I spelled that wrong. You use conscious door is not right. Let's just check. I got the name right. Use council store from app slash stores slash council. Say that access the store, we need to assign it to a constant or a variable. I'll scroll down to the bottom here. I'll put our council comments bucket. We can just do const and give it a name. Let's just call it counter, set that equal to use counter, store parentheses. And we should now be able to access everything that's in our council store by using this counter cost. I'll save that. And we should now actually be able to see our counter store in our view DevTools, we want to open up the Chrome dev tools by going to View Developer Developer Tools or using the keyboard shortcut. Then we can click on this little arrow and choose View. Reload the page for some reason view DevTools isn't working. I'm just gonna try quitting Chrome and restarting it, see if that fixes. It. Just jumped to the terminal. Command. Click on this link to open up the app, open up the DevTools, click on the arrow and go to view. It seems to be working. Now, let's just stretch this out a bit. Stretch this out. If we go to this little icon here and Penny Are, we can see our counter store. And if we click on that, we can see the state properties in it. You could say count and we can say title. Now let's see if we can use this count state property on our page. I'm just gonna show up this back over here. Move this over a little bit. We assigned our store to this counter constant. So to access this data property counts, which is an R-state, we should be able to just do counter dot count. So let's scroll up to where our counter is. Select this 0, a double curly braces, then counter dots count, save that, reload. We can still see 0 there and we can actually modify our state properties using the penny a dev tools within the view dev tools. So we can either click on this pencil and enter a number. And we see that update. We can use these little plus and minus buttons as well to change the counter. Not all seems to be hooked up correctly. And if we jump to our counter store and change the value, save that reload, we see it's now set to ten. Let's change this back to 0 and save that. For some reason the hot reloading, it doesn't seem to be working at the moment. Now there is a page on the penny, a site which tells you how to enable it. And I've tried following that before and I couldn't get it to work, but let's just have a lot of Go since it might have been updated. I will just Google penny on jump to Penny app.vue js.org. Jump to guide, will jump down to Module Replacement. It says blah, blah, blah. You need to add this snippet of code next to any store declaration. Let's say you have three stores of GIS, cart JS, and charge JS, you will have to add an adapt this after the creation of the store definition. Makes sure to pass the right store definition. Use off in this case, let's just copy this snippet here and we'll paste it after I use counter store. Guess we need to replace this user off with use counter store. Paste that in and save that reload. We have an error here, uncaught ReferenceError accept HTML update is not defined. Let's jump back to the example. We need to import this, accept HTML update from opinion as well. So I'll copy that and give it a try. Save that reload. Let's see if the hot reloading is working now I'll change the counselor to ten, save that, and it still doesn't seem to be working. Maybe we need to kill out that process and start it again. I'll kill it up process with control and say run npm, run dev, reload the app. Let's change this to 100, and it still doesn't seem to be working. Hopefully this will be fixed at some point. And if I find a fix, a lot of video to the course, but for now, I'm just going to remove this code that we just started. And as it doesn't seem to be working. Also, if you know how to get it working and please leave a message with this lecture. So let's remove this except HMM, update as well. Counter back to 0. For now, we're just going to have to keep reloading whenever we make changes to our store. Now let's hook up the counter title. We'll jump back to home view dot view, select this text hard-coded title, delete that are double curly braces. And again, to access our store, we can just use counter. And then to access the title property, we can just do dot title, save that. And we can see my counselor title on the page. And if we change it in the dev tools here, then we should see it update as well. And that's working. Let's look up this input as well, which is here. We'll just add V-model and set that to the same thing, counts, dot count. Save that. Actually no, this should be set to the title. So we want counselor dot title, save that. If we update this title in the input, we say update in this heading as well. And we also see it update in the DevTools. 57. Pinia - Actions: Let's add some actions through our opinions store. In opinion store actions are basically just methods which can access the properties in our state and modify them. So actions are the equivalent of the methods objects in the Options API or any methods which modify your reactive data in the composition API, we just need to add an object after the state called actions. Oops, that should be auctions. And we can place all of our actions in here. So let's create an action for increasing our counter. So I'll create a method here called increase counselor to access properties in our state, we can just use the, this keyword bit like we did in the Options API to access our data properties. So we can just do this dot counter plus, plus. This oxygen should be good to go. So let's add a comma, duplicate this action, get rid of that comma. And we'll set up an action for decreasing the counter called decrease counter, will just say this dot counter minus minus. And I've just realized this should be count, not counter. So I'll just change that to count and change that to count as well. Save that. And we should now be able to use these actions. So let's jump back to whole view dot view, and let's jump to this first button. After the counter, I'm allowed to click handler. So app click equals. To access the store, we can use counter, which is the name of the constant we've used here. And then to access the action, we can just do dots and then the name of the action. So increase counter, save that. Let's see if that's working. I think we might need to add parentheses at the end here. So let's try that, save that, and we have some errors here. We might just need to reload. Let's try that again. Let's see if we do need to add these parentheses. Actually, I'll save that. Reload. Actually, we don't need to add the parentheses. Let's jump to the minus button that's directly before the counter. And let's just copy this click handler. Paste it onto that button, will change this to Counselor dot decrease counter, save that. Both of these buttons are working now. And we can also send parameters to our actions as well. So if I just jump back to counter dot js, if we want these to receive parameters, then we can just add a name for that parameter in here. So let's call it amount. Let's say we want to increase or decrease the counter by this amount that we're passing in. To increase it by this parameter, we can just do this dot count plus equals amount. And then to decrease, it will just do this dot counts minus equals amount. Save that. If we want to pass a parameter in, you just stopped parentheses at the end of the method name for this button, it will decrease it by one. And then for this Boltzmann will decrease it by one less also hook up the other buttons as well, this one and this one will make that decrease by two. Increase by two. I'll copy this click handler from the plus button, paste it onto this one. We'll just change the value we're passing into two. Then we'll do the same for the other decrease spots it, copy the click handler, paste it in here, change the number to two. And let's say that now hopefully all about voltages should be working. Yeah, we can decrease it by one and increase it by one. These ones are not working. You might just need to reload again. Yeah, that's not working. We can increase by two and decrease by two as well. And we can also see the data changing in DevTools as well. 58. Pinia - Getters: Getters allow us to get a value from our state and then modify it in some way or generate something based on that value, and then return it and making it available to any component which is using this store, you can think of gets us as the state management equivalent of computed properties. So the return values of getters are cached and only updated whenever their dependencies change, whenever the state properties that the getters are using changed. So let's create a getter for splitting out odd or even here, which is currently hard-coded based on the value of the counter, this count property. To start adding debtors, we just need to another object called getters. By the way, it doesn't matter what order we put these in. If you prefer, you can put the getters after state and then auctions at the bottom. It doesn't really matter. But anyway, let's add a get-up which works out whether this number is odd or even, and then returns the texts odd or even. So we'll create a getter called odd or even. This should be a property which we set to a method like this to access our state, we can just pass in the state as a parameter in this method. So to grab this count property that's in the state, we can just do state dot count. We want to find out if this is odd or even. So again, we'll use the remainder operator to do this. It can just do if state dot count remainder two is equal to 0. In other words, if we divide the count by two and we get the remainder, and that remainder is equal to 0, then we know that the number is even so we can return the text, even. Otherwise, we can return the text autonomy. It's important to note that in order for gets us to work, they do need to return a value. So let's save that reload. And we should be able to use this on our page. So let's jump to our home view dot Vue. Get rid of this odd slash. Even texts are double curly braces. We can just do counter dot odd or even. Save that. And we can say the counter is even, we change it to one. We see the counter is odd. If we're also looking at dev tools, we can see our getters here as well, and we can see the value of that changing as well. One other advantage of pennies over a few x, which I didn't mention earlier, is that we don't need to specify state actions of getters when we're using these in our template or in our scripts section. So in a view x up, we'd have to do counselor dot state, dot title. And then when we trigger an action, we'd have to do counter dot action, decrease counsel, etc. In opinion Europe, we don't need to specify these, which makes our code a little bit less verbose. 59. Pinia - Use our Store Anywhere: Now that our opinions store is set up, we can use the state actions and gets us anywhere in our app. Let's use this store on our posts page for this counter. This counter is currently using use counter composable that we created earlier. So let's remove that from this page. And all the dynamic functionality and hook the spots are not too new. Penny counts or store. Let's jump to this page. Views and posts view dot Vue will remove this import of the US counter, composable. And also remove this const wherever grabbing the stuff that we're using from the composable. And then I'll jump up to the template. Let's replace this counter data dot counts with the countless in our store. So counter dot count. And let's replace this method here, increase counter to the increased counter action that's in our store. We can just change this to counter, dot increased counter. Since the method has the same name. This class that was spitting out based on whether the number is odd or even, we can just replace this odd or even with the getter that's in our store. So counter dot odd or even. So let's change that counts dot odd or even, save that reload and we have an error here. What's going on? Property counter is access during render but was not defined on instance. Let's just scroll down. We forgot to import our store. Let's jump to home view dot Vue. Copy this input line where we're importing the stool, paste it onto posts view, dot view. And then we also need to assign this to a variable. So I'll copy this from home view dot view, paste that here. I'll just remove that comment, save that and we should be good to go. So let's reload. Button is now working. We can say in the dev tools it is modifying the countless in our opinion store because our opinion store is global. Them. When we jumped to the homepage, we should see that the count is set to eight. It is. If we increase the counter here and jump back to the posts page, we can see that the value of the counter is always up to date regardless of where we are within our app. 60. Class Project: Noteballs [Module 13]: We're going to spend the rest of this course taking everything we've learned to create this real-world note-taking app called Nope balls, creating this up, but allow us to take all of our new knowledge of the basics of the composition API and solidify that knowledge with the creation of a real-world out from scratch. In this app, we can display a list of notes. Each node has the note on the number of characters and an edit and delete button. We can add a new note. We can edit a note, save it, and we can delete a note as well with this conformation modal. And we also have a header with links throughout two pages, notes and stats. And we have this Stats page where we display stats related to our notes, including the number of notes we have on the total number of characters of all our notes. It's also going to look presentable as you can see by using the bowler CSS library. And it's also gonna be responsive as well. On desktop we have these links to our two pages, but if we scale this down, we can see all of the elements scaled out for mobile and our links in the header changed to this little bigger icon. And when we click on that, we can see the links to our two pages. This app is going to make use of almost every concept that we learned earlier on in the course, including view router on the US routes and use router composable. This time we're actually going to install Vue Router and set it all up from scratch, is going to use reactive data including refs and reactive objects is going to use methods computed properties, and watches is going to use child components including props, emits, and update model value is going to use template refs on composable, including custom composable on a composable from the few use library is going to use directives and lifecycle hooks. And we're going to set up all of our app's state management using pennies. Although this time we're actually going to install penny a, setup our penny a store completely from scratch. And of course we'll also be adding state actions and a whole bunch of getters to our penny a store. 61. Create Project: Let's create a brand new view, three projects for our note balls up. Let's jump on over to the V3 website. I'll search for V3 joked to v3 dot Vue js.org. And earlier on in the course, I told you that the docs are about to be massively revamped and I had to change the URL to get to the new ducks. But it looks like the New docs and our lives, we can just jump to install. Scroll down. Again, we need to have no JS installed. What you probably already have that installed at this point. Let's create a new view project with this command. Copy that, jump to my terminal and VS Code and a gamete can toggle this with control and back tick. And I'll make sure you're in the folder where you store your projects. You definitely don't want to run this in the previous projects folder if that's where you are, I'll paste that in and launch that. I'll type in y to agree to install, Create view for the project name and the project folder. I'm gonna use view dash composition API. Nope balls. We can use any folder name you like. I'm going to choose node to all of the options. We're not going to install Vue routes to this time because it's going to install that from scratch instead it all up from scratch. So no, and again with pennies, we're going to set that all from scratch. So I'm going to choose No, no, no. You might want to consider using yes Lynn in your own projects to help you ensure code quality. But for simplicity, I'm just gonna choose note to this. And we have some launch steps here. So we just need to cd into the folder and run npm install to install the dependencies and then run npm run dev. And I can see the folder is created here, composition API nope balls. So I'm going to drag that into VS Code. And then open up the terminal and run npm install. Then npm run dev to launch it. Now we can Command and click on this link and we can see the basic app running in the browser. Before we get started, let's just change the name that appears in the top there. So I'm going to open up the Explorer and go to index.html. I just changed the text in these title tags to note balls. Save that, and we're now ready to get started creating nope balls. 62. Router - Install & Setup: While there is an option in the build tool we just use to add Vue Router to our project automatically. It's good to know how to do this manually just in case you have any to add Vue Router to an existing view. Three projects which doesn't already have it sets up. Let's add Vue Router to our project and get it all setup. So let's jump to our terminal and kill the death process with control and say install Vue Router. We can just run npm install Vue Router four to get the latest version of your router that's now installed. We can launch our project again with npm run dev, close the terminal. Throughout the router to our API needs to jump source and main.js. We cannot be routed to our app by importing the Create Router method from Vue Router and adding it to this creates a chain with some options. First of all, after this first line, we need to import Create Router from router. And we can now use this create routes or method to setup our router. We need to assign this to a constant. So we'll create a constant called router and assign this to that. We want to pass it an objective with all of our router options. The first option we need to add is the history mode. We can set this to either create web history or create web hash history. Now create web history will use real URLs in the browser when we navigate across all pages. For example, if we went to an About page than the path would just be slash about. Whereas with web hash history, we will say slash then hash, then slash, and then about when we go to our about page, and I'm going to use the web hash history here, because when we're using this mode, we don't need any fancy server setup to get our app running if we decide to deploy it to a server, let's set this history property to create web hash history. We need to import that from Vue Router VS Code is automatically added that for me here. But if yours hasn't unjust, add it to this impulse. The next option we need to add is our routes. We create a property called routes setup to an array. And we can set up all of our routes in here. But before we do that, let's add this router instance to our app. So after this, create our app, we can add the US method to this chain dot use parentheses, and we can just pass in this router constant. I'm just going to break this up onto multiple lines and save that. Let's make sure we're not seeing any errors. I'll reload. And I think this arrow might just be because we haven't settled any routes yet. Let's do that next. 63. Router - Add Some Routes: Let's set up some routes for our app. And our app is going to have a notes view, our page, and a stats view. Let's set up a route for our notes view first. Well, first of all, we're going to need a component that we can use for that notes view. Let's jump back to the File Explorer and we'll right-click on the source folder and create a new folder called views. This doesn't have to be called views. You could call it pages if you'd like, but I'm going to call it views. And let's create a new component in there, and we'll call this view notes. Don't view template tags. For now, I'll just start a div with a class of notes. Inside that, a lot of H one that just says notes and save that. And we can now use this component for our notes view. Let's jump up to main.js and add a route for our notes view. For each routes in our app, we need to add an object with some options. First of all, we need the path option. This will determine what path will take us to this route. I'm gonna set the path to slash follow the notes view, which just means this will be the root route of our app when we first visit are up, this is the route that will go to. We also need to add a name property. I'll set this to notes on this name property makes it easier for us to send the user to a particular route programmatically. And finally, we need to add the component property which we just set to the component that we want to use for this route. So first of all, we need to import our component. So we'll import view notes from slash views, slash view notes dot view. Now in previous view builds which used Webpack, we didn't need to put the dot view here, but when we're using VT, which we are now that we do need to adopt view. And also this at symbol here. This is just an alias which refers to this source folder. And this alias is being defined in this vt dot config dot js file. We can see that here. You can also add your own alias introduction to different folders if you want to. But anyway, let's jump back to main.js and we can now set the component for this route. We'll set this to view notes. Let's save that and reload. We still have an error here. And I've just figured out what the issue is when we set the history to create web hash history, this needs to be a method call. We just need to add parentheses to the end of that, save that the app is now working again. However, we can't see this notes view that we've just created in the browser. And that's because we need to add a router view component to our app, to our root component app.vue. And we'll do that in a minute. But before we do that, let's just set up a route for our stats page. So let's jump back to the views folder. Duplicate view notes, dot view. We'll call this view stats dot view. I'll just change the class on the day of two starts. We'll change the text in the heading two starts as well. Save that. Jump back to main.js. And let's add a comma after this route and duplicate that. By the way, I did that by selecting this texts and pressing Alt Shift and down, the shortcut might be different on your platform. Now let's get rid of this comma here. Almost set the path to slash stats so that when we go to slash, that's we get to the stats. It changed the name to stops as well. And now we need to set the component to ask dots view, but first we need to import it. So I'll duplicate this view notes line and we'll import view stats from art slash view slash view, stats dot view, and save that. Now we just need to change this component to view stats, say that, but we still can't see on Notes view on the page if we go to the path slash and we can't see the stats view if we go to the path slash stats. And that's because we need to add a router view component to our app. Let's do that next. 64. Add RouterView & Navigation: Let me go to the path slash. We don't see you notes component with this heading with the text notes. And that's because we need to add a router view component to our app is the router view component which determines where our routes will be displayed. So let's jump to our root component source, app.vue. And I'm actually just going to remove this script section, remove everything inside the template, and remove the style tag and all of the styles as well. Save that. And we can now also delete all of the components in the source components folder. I'll select everything in here, right-click and delete. We see an arrow, but if we reload, then the error disappears. Now to display our routes, we just need to add a router view component. And we can do this in dash case like this router view. We can do it in Pascal case, which I'm gonna do. So router view, save that. And we can now see our notes route on the page. And if we go to the path slash stats, we can see our stats view. Now using the address bar to navigate isn't much fun. Let's add some router link components to our page so that we can navigate above this router view component. I'm going to add a router link component inside that, I'll put the text notes and then I'll duplicate that. In this one, we'll put the text stats to determine where these router links will go. We can just add a two prop and set it to the path we want to go too far, the notes view, we can just set this to slash. And then for the stats view, we can just set this to slash stats. Let's save that. Let's just add space after the first one and a pipe and then another space. Say that we can now navigate our app. 65. Router - Tidying Up: Our router is now all sets up and work in. But before we move on, let's just tidy up our code a little bit. So first of all, I'm just going to extract all of our router setup code into its own file in the source folder. I'm gonna create a new folder called router. In there. I'm going to create a new file called index.js. Let's jump back to main.js. I'm going to use a multiple selection by holding down the Alt key. Although the shortcut might be different on your platform, I'm gonna select all of this router, const the two inputs for our notes view and stats view. This line where we're importing the Create Router and great web hash history methods from Vue Router. I'm going to cut out a lot and just tidy this up a little bit. And then jump to index.js in our router folder, paste all that code in, and we'll just start a line after our inputs. And now we want to export this router instance to make it available to other files. So at the end we can just do export default router and save that. And now if we jump back to main.js, we can import this router instance by adding import router from slash router. And we don't actually need to add slash index.js when we use the filename index.js, it will always look for this file automatically. So we can just do import router from slash router. And now when we fire use router, we're using the router instance from index.js file in the router folder. Let's save that and see if everything is still working. It's all still working. One more thing, I'm going to jump to index.js and I'm going to put all of these routes into their old constant. So I'm going to set up a constant called routes. Set that equal to an array. Then I'm going to cook these two objects, paste those in here. And now we can just set this routes property, two routes. Or since these names are both the same, we can just shorthand this to just routes. So let's save that, make sure it's still working. Yet. It's still working well now we've tied it upon main.js file. I moved all of the code related to our router into its own file. 66. Bulma & Design - Install Bulma [Module 14]: To help us make our outlook pretty, we're gonna use the Bulmer CSS library. I'm going to jump to Walmart.io. Now this isn't a CSS course, so I won't be focusing on bulla and CSS to March. We just simply using it to help us make our outlook pretty without having to spend tons of time writing CSS excess. And that way we can purely focus on Vue.js three and the composition API. Also, I'm not necessarily suggesting that you should use Bulmer for your projects. You should make an informed choice based on your individual projects needs. However, I believe bomber is currently the third most popular CSS framework after bootstrap, tailwind CSS. However, I've chosen bola for this project because it's the easiest CSS framework to get started with. And it has all of the components that we're going to need for this project. If I jump to documentation and components, we have this card component that we're going to use, this Navbar component that we're going to use, the modal component. Now we're going to use, has a bunch of farm components that we're going to use like this input. And it also has a nice button component that we're going to use as well. Let's get bullet installed to our project. I'm going to jump to overview and then start scroll down. And there's a few ways we can install it. We can install it using a CDN link where it will be loading in externally. We can download it from GitHub. We can install the npm package. This is the way I'm gonna do it. So we need to run npm install Puma. So let's copy that. Open up our terminal, kill the death process and run npm install bulla. And that's installed. And if we jump to our node modules folder, we can say Bulmer is here. And if we jump into the CSS folder, we can say bola CSS files. I'm going to use the minified version, Bohmer dot main.css. What we could also use the minified version form dot CSS. I'm going to right-click on bullet dot main.css. Click on Copy path that I'm going to jump to app.vue. I'm going to add a style tag to import that CSS file. I'm going to add at Import. Then I'll paste in that path. We have the full path in my computer here. We only need the path from this bowler folder here. So I'll remove everything before that bullet folder. We need to add a semicolon at the end. Let's say that we need to relaunch our app. Let's run npm, run dev. Jump back to our app. We can see our styles have already changed a little bit, but just to make sure Bulmer is working, Let's add a class to each of our router link components here of button. And this should give this a nice button style. Save that. We can see some nice little buttons now, and we can now start using Bohmer to make Kiara up look beautiful. 67. Nav Bar - Design: Let's add a nice, beautiful navbar to our app. Now first of all, let's just create a component for it which will import into app.vue. And for some reason my components folder has disappeared. But you should have a components folder here. But I'm going to create that components. Inside that. I'm going to create a folder called Layout. I don't like the way VS Code groups are folders together like this by default. So I'm just going to fix up. I go into code and preferences and settings, and I'm going to search for compact. We could just untick this compile folders option. And we now have a normal folder system here. Inside the layout folder. I'm gonna create a file called navbar dot view. For now we'll just start out template tags. Just add a div with my navbar, the text. Now let's jump to app.vue and import this component scripts Up tags. I'm going to add a block comment here which just says imports. Then we'll import navbar from slash components slash layout slash enough bar dot view. I use these big comments to split up my code into sections. I just find it makes it a lot easier for me to scan through my code. Well, this is entirely optional. You don't need to add these comments if you don't want to. Now let's stick this Navbar component on the page. I'm going to get rid of these routes of view legs for now and just output our navbar. Let's save that. And we can say my navbar on the page. Now let's jump to the baldness site and we're gonna go to Components. Navbar, scroll down, stretch this out a bit, and we're just going to copy this example here for a basic navbar and then we're going to modify it. So I'm going to copy all of this code. Move this back over, and then we'll jump to Navbar dot view, paste it all in here, indent that and save. And we can now see navbar. I'll just zoom out a little bit. I want to end up on a nice green color instead of white. And we can do that by adding a class to this NEF element when to split the attributes on that using the split HTML attributes extension which we installed earlier on. A lot of class of success, save that. We now have a nice green Napa. And again, I'm not going to be explaining all of these classes from bullets very much because I want to stay focused on view and the composition API as much as possible. But you can find all of these available classes on the site. For example, for this navbar Dropdown, two colors shows you how to add all of these different colors here. 68. Nav Bar - Navigation & Logo: Let's customize this navbar. First of all, I want to get rid of this More link here with this drop-down. Let's scroll down enough Bardot view and see if we can find that. We can see that more link here. And we can see this div with a class of navbar. Let's collapse that div, just get rid of it. Save that and we see that disappear. Now let's move these two links over to the right-hand side. So I'm gonna scroll up on these two navbar item links here. I'm going to cut them and I'll just get rid of this navbar dash stop Dip. I'm going to paste those links into this navbar end div, which will put them over on the right. Let's collapse this dip inside that, not by item, and get rid of that. I'll just paste in the links we cooked before and save that. Let's make these links two notes view and our staffs view. I'm just going to remove the second lab bar I2. Now in this first one, I'm gonna change this to a router link component. Makes sure we change both the opening and closing tags that I'm gonna split the attributes on this, change the text to notes. I have a lot of to prop to this router link, settle out to the path slash, which is where our notes routers. And then I'll select this router link, duplicate it. And we'll change the two prop to the path slash stats, and we'll change the text to starts as well and save that. We now have links to our notes and stops page. We can see this heading of dating. Paula has a class which is active, which we cannot to one of these navbar items to make it appear active. You can see this link now looks active. These routes link components can actually add a class to the link when that link is the active link. If I just caught that class, we can just add a prop of active dash class, set it to. This class is octave, and I'll also copy that and paste it to the stats link. Say that we should now have active places on our links. And yeah, that's working. Now let's just replace this bomb a logo with a title for our app. I'll scroll up to the top and inside this navbar brand div, we have this attack with a class of navbar item. I'm going to remove the image that's inside that and remove this H ref. I don't want to change this a tag into a div because I don't want this to be a link. And then inside that I'll just add the texts nope balls. And save that to improve the style of the text here, we're going to add a class to this day of, of size four to make it a bit bigger. And then a class of family monospace to give it this monospace style font. And again, you can find all of these classes on the bolus site, which is very well-documented. 69. Nav Bar - Responsive Design & Menu: Now currently the content of this navbar will keep stretching out as the browser gets wider and wider, which doesn't look too great, especially on really big screens. We have the logo right over here and then these links right over here. I'd like all this to be constrained in the middle after a certain point. And we can use boneless container elements to do this. So I'm going to surround everything inside on navbar with a div, with a class of container. So I've just minimized everything that's in there. A lot of div with a class of container. And to make sure it constraints once the browser is wider than a certain number of pixels, we can add a class of is dash marks, dash desktop. And these classes are all documented on the ball of sight. And then I'm just going to move this navbar brand, this navbar menu into that div and save that. And we can now see a larger screens. Everything is constrained to the middle of the page. Now if our view gets below 100 to four pixels, we see the nav links disappear and we see this, the old beggar icon, but nothing happens when we click it. That's because in order to show the mobile menu and change this icon into its active state, we need to add a class of active to both the burger icon and the nav menu as well. Now this is all documented on the navbar page. But just to demonstrate this, let's hardcode those classes in. Now, I'm going to split the attributes on this a tag with a class of navbar big. I'm going to add a class of active and save that. And we can see the icon change to an X. And if we scroll down to this navbar menu div, I'll also split the attributes on that a lot the same class to this div. It save that. And we can now see the mobile menu appear. Let's set up a href to toggle these classes. When we click on the burger menu, I'm going to remove these hardcoded classes for now from the navbar menu div on the navbar Berger a tag, say that unless jump down to our script section, which doesn't exist yet. So let's create it Scripts setup. Lot of block comment which says mobile nav. Almost setup a constant called Show Mobile Nav. Set that equal to a href with an initial value of false. This is false. We won't show the mobile nap when it's true, we will show it and we need to import the ref method from view. So I'm just going to add a quick comment here. Imports will import ref from view. Save that. Now Let's conditionally add those classes based on this href. So let's jump to the burger icon first, which is here with a class of navbar dash burger. Now let's bind to the class attribute by adding colon class curly braces, and a lot of classes, A's dash active. But only when Show Mobile Nav is true. And then I'll copy this and also add it to this div with a class of navbar dash menu. And I'll save that. Now if we jump down to our ref and change the value to true, then we can now see the mobile nav on the active version of the burger icon. So let's set that back to false. Will toggle the value of this href. When we click on the burger menu, will jump up to the navbar burger tag and add a click handler. And when we click this, we're going to set Show Mobile Nav equal to the opposite of Show Mobile Nav. If it's false, we'll set it to true. And if it's true, we'll set it to false because this is an a tag is going to try and go somewhere when we click it. So to stop that from happening, I'm just going to add a modifier of dot prevent. This will prevent the default behavior of the element we are clicking on the default behavior of an a tag is to go to another page. Let's save that and see if that's working. Click on the button and we see the mobile nav appear. Click it again and we see it disappear. Now, I don't like the fact that our mobile menu is pushing our page content down. See how it's pushing this heading now, I would rather it was just positioned absolutely over the top of our page. Let's just add a little bit of style to make that happen. Let's scroll down to the bottom of the style tag. Lot of media query here for mobile media, max-width 1023 pixels because that's when the mobile nav styles kick in when we scale down the browser. Now we want to target on enough bottom menu, which is this div with a class of navbar menu. So I'm just going to add dash, dash menu, setup to position absolute and set the left to 0 and set the width to 100%. Save that click on the button. The mobile menu is now positioned over the top of our content instead of pushing it down. 70. Pages (Design): Before we start working on the design of our notes page, the main page in our app. First of all, I'd just like to constrain the content of all of our pages. I'll also add a little bit of padding because right now we can see the content of our pages is right up in the corner with no padding. And also a constant is always right over to the left. Regardless of how wide browser is, which doesn't look too pretty. I'm just going to use the same classes that we use to constrain the content in our Napa container and is Mac's desktop classes. And so to do this, I'm gonna jump to our App.vue. Just surround our router view in a div with these classes. So container is max desktop. Just move the router view into that and save that. We can say our content is now constrained. Bona has some padding classes we can use to add a little bit of padding around our pages as well. And these are all documented on the ball must cite a lot of classes of Px dash T2 to add some horizontal padding and then a cluster of P, dash four to add some vertical padding. This is looking good now on smaller screens, but on wider screens, our logo isn't lining up with our content. I think it would look better if this lined up. So let's add some padding to our navbar as well. I'll just do navbar dot view and then this container div that we added. And a lot of classic PX. That's looking pretty good. Now, let's give all of our pages a subtle background color which matches the color of our navbar. So I'm going to jump to the File Explorer and open up index.html. I'm going to add a class to our HTML element. Bomber has tones of color classes we can use, and they're all documented on the palmar side. Well, I'm going to use the class has background, dash, success, dash light. Save that. We now have this nice social green color in the background of all of our pages. 71. Notes (Design): Now let's create the design for our notes using bolus card component. I'm gonna jump on over to the boldness site and go to Components and cut our full-screen this a sec, I don't want to scroll down this example here, which has these buttons at the bottom, which we can use to add the ability to edit and delete a note. I'm going to copy this example here by clicking on copy. And then I'll jump back to note balls. We're going to add this to our notes view. So we're going to go to Source Views and view notes don't view. And I'll paste that inside this div over the top of this one. Indented a little bit and save that. And I'm gonna get rid of this header at the top. So that's this header element here. So we'll get rid of that, save that, and I'm just going to enable word wrap by default. So I'm going to go to code and preferences and settings, search for word wrap and set that to on by default, that's better. Now I'm gonna get rid of everything inside this div with the class of content. So get rid of that. Spits out some Lorem by just typing in lorem and then hitting Enter. You should see this Emmet abbreviation. Save that. I'll just get rid of this save button because we're only going to need an edit on the delete button. Save that, and let's just see how this will look with multiple cuts on. A quick trick to do this is to just add a v4 to the parent div. So before then we can just do, I just spit up three of these. And you can see we need a little bit of space between each note. So a lot of class to this div with a class of card of M, beta margin, bottom dashboard. Save that. These looking pretty good. Let's just make sure they look okay on a big screen or on a small screen? Yeah. Pretty good. 72. Add Note Form (Design): Let's start to fall to the top of our notes view with a text area on a button that we can use to add a new note. So back to the ball mill site and we're going to go to form and general. I'm going to scroll down to complete farming sample, show the code. All I'm going to grab here is from this text area down to these buttons. So let's scroll down the code that has a text area. And we do want this div with a class of fields that surround it. So I'll select from there to this depth down here with the two buttons inside it. Copy that. Paste this above our notes. It's still inside this div with a class of notes. I'll paste that here, indented a little bit and save that. Now I'm going to remove this label here with the text message which is here. So get rid of that. And I'm gonna get rid of this checkbox here to this div with a class of field. So we'll get rid of that, will get rid of these two radio buttons, which is this div here. Save that. Now I'm going to surround the text area and the buttons in a card component with a background color. So I'll stick that will pair. So a lot of div with a class of card to give it a background color, we'll add a class of has background success Doc. I'm going to select these two divs with a class of field, cut those, paste them inside this div, save that, we need to add a bit of padding here. So we're gonna add a class of P dash four, give it a bit padding and we need a bit of margin at the bottom. So a lot of classes, MBA, margin-bottom, dash f5, save that. Now I'm going to remove this cancel button. So I'll remove this div with a class of control which has the cancel button inside it. Save that. I don't want to pull this button over to the right-hand side. You can do that by adding a class of is dash, dash, right to this day of the class of field. Save that. And I want to change the color and the text of this button. I'm just going to break it up onto multiple lines and I'll change the text inside it to new notes. Say that we'll change the background color by adding a class of house dash, background, dash, success, save that. Finally, let's change the placeholder on this text area. And in fact, I'm going to change this into a self closing tag like that. Split the attributes and we'll set the placeholder to new note, save that this is looking pretty good on desktop. Looking pretty good on mobile too. 73. Notes Array (Ref) [Module 15]: Okay, So our notes page is looking pretty gosh down beautiful. Now let's get this up to actually work by adding some data and methods. We're also going to turn this note into a child component. We're going to add a computed property for displaying the number of characters in each note. And we're also going to set up a custom event using emit. Well, first of all, let's set up a href where we can store a list of notes in view, notes dot view. I'm going to scroll down to the bottom and add our script setup tags. I'm going to create a block comment here which says notes, sets up a constant called notes, set that equal to a href. We need to import this ref method. So a lot of other block comment here which says imports. And again, these comments are optional. You don't need to add them if you don't want to. We need to import ref from view. Now let's setup our notes array. So we'll pass an array into this href. Each item in the array will be an object's allowed an ID field. We'll just set that to add a lot of concept field for the note content. I'll just stick some lorem in there. For some reason M, it's not working right now. I'll just copy this Lorem from a hard-coded note. Paste that in there. A lot of comma after this object. Duplicate the notes, get rid of this comma, set the ID to id2. I'll just change the text on this one. This is a shorter note. We save that. Now let's display these notes on our page using a V4 directive. We already have a V4 directive on this div with the class of card which we're using for our notes. So I'll just change this to be for note in notes. So we'll use notes as our placeholder as we loop through this notes or write. To set up here. We also need to add a key property. Almost set this to note that this k is always unique. I'll save that. And we can now see two notes on our page. And we just need to output our content, which is gonna be out notes dot content. I'll select this text here in this table, the class of content or double curly braces. And then note dot content. Save that. Reload. We can now say A2 notes on the page which are now coming from our notes array. 74. Add Note Method: Let's set up an ad notes method which gets fired after we type something in this text area and click the button, which then adds this new note to our notes array, those displaying it on the page. First of all, let's set up a href, which we can bind to this text area so that we can actually get access to the content that the user has typed. Above our notes array, I'm going to set up a new column called New Note. Set that equal to a href with a default value of an empty string. Now let's find this constant to our text area. So we'll scroll up to the text area which is here. Out of V model directive. Set that to new note. Save that. Now if we type something into this href value, hit Save that. We can now see it in the text area. Let's set that back to an empty string and save that. Now let's set up a method which is fired. When we click on this button, I'll jump up to the Add New Note button, which is here. I'll split the attributes on the opening tag. Add a click handler. Click will fire a method called add notes. And now we need to create this method. So we'll scroll down after our notes array setup this method const note equals an arrow function. For now, I'll just log out ad note, save that. Open up the console. Click on this button. We can say this method is being fired because we can see this log being fired. Now we don't want the user to be able to click this button if the text area is empty, because suddenly just be adding an empty node. Let's disable this button when there's nothing in this text area. Let's jump up to this button, which is here. We can just bind to the disabled attribute and conditionally add this disabled attribute to the bottom. We only want to add this attribute. This new note, href is an empty string. To do that we can just do not new note. Because if that string is empty, it will count as a 0 value or a falsy value. So let's save that. We can now see this button is disabled. We can't click it. And if we touch something, we can now click it. Now let's work on our notes method. First of all, we need to an object with an ID and a content property with the concept properties set to the content that's in the text area. And then push the object to these notes are right. We should then see the notes on the page. First, let's set up this object. I'll create a variable called note. Set that to an object. And we're going to need an ID property. The concept property. Now the concept property we can just grab from this new note, href, which is bound to the text area. We can just set this content property two. New Note dot value. Now for the ID, we need something unique. If you'd like, you could use a package for generating a unique ID. Uuid. I'll just Google UUID npm. I usually use this UUID version for package. You can use that if you like. But just for simplicity, I'm just going to use the timestamp in milliseconds from the current date to generate this ID above this object, I'm first going to grab the current timestamp, so we can just do new date parentheses. And this will grab the current date as a date object. And then to convert that into a timestamp in milliseconds, we can just add dot, gets time, parentheses. And now I'm going to assign this to a variable. So we can just do, Let's current date equals. Now this will actually return an integer. I'm going to convert it into a string because we're using strings here. I'll set up another variable called ID. Almost set that equal to current date. Dot two string to convert that number into a string. If we want a shot him this a bit, we can get rid of the word let an indent law a bit. I'm just not a comma. After the first variable. We can now just set this ID property to this ID variable we setup here like that as a shorthand since these names are both the same, we can just set this to ID like that. Now let's just log out this note and make sure this is working. Console. Dot log notes. Save that. Let's add some text here. Click on the button. We can see this object being locked out. Yeah, we can see the content property and we can see the ID as well, which has been generated. Based on the timestamp of the current date. Now all we need to do is push this object to note. So right here we should see the new note on the page. To get access to this notes, href, we can just do not's dot value. We can just do note stop value. Then to push this object to the array, we can just do dot push. Then note. Let's save that and see if that's working. I'll type in a new note. Click the button. Yet we can see the note added to the page. Actually it makes sense for our latest note to be at the top of the list. Instead of pushing it will unshift it, which will basically just stick it at the start of the rights that at the end of the array, I'll just change the word push to on, lower-case on shift. Save that. And let's try that again. Yeah, that's working now. It's adding it to the top. Now after we add the notes, we can see that the text area still has the same content in it that we just added. As a note, Let's clear out this text area after we add the new node by just simply setting this new note href, which is bound to the text area back to an empty string. So we can just do new note, value equals empty string. Let's see if that's working. Click on the button. We can see the text area is emptied. We could also use a template ref, to refocus this text area after we add the new note as well. So that the user can easily add another note straightaway without having to click into this text area. Let's jump up to the text area elements here. A lot of ref. I'll set that to new note ref. We can now use this href to focus this text area after we add the new note. So let's jump back to our add notes method. The bottom here, we can just do new note ref, dot value to grab the actual text area element. And then we can just do dot focus. This should hopefully focus the text area after we add the new note. Let's save that and reload. Make sure that's working. Hello. Add a new notes. This hasn't worked. New note ref is not defined. Let's just scroll up and check the href that we added. You know, href. We need to actually set up a data before we can use this template ref. After this new note const, let's add another column set out to new note, HREF, set that equal to f with an initial value of null. Save that. And let's reload again. Hello there. Click on the button. That seems to have worked. Now we've added the new notes. We've cleared out the text area, and we focus the text area. 75. Child Component - Note: This view notes component is getting quite long. Now, we're gonna be adding some more functionality to each individual note, such as displaying the number of characters, displaying a modal when the delete button is clicked and sending the user to the edit page when the Edit button is clicked. So before this view notes component gets too messy, Let's create a child component for an individual note. I'm going to jump to the Explorer and go to source components. Let's create a new folder here for all of the components related to our notes. We'll call this notes. Inside that I'll create a new file called a dot view template tags. And I'll jump back to view notes dot view. Almost select all of this div with a class of card which is displaying our note. Just copy that. Paste it into the template tags. Fix the indentation. I'll just remove the V4 and the key attributes. Save that. Now let's jump back to view notes dot view, and import this component. In our input section, we'll do import notes from slash components, slash notes, slash note, dot Vue. Save that. Now let's scroll up to this div with the class of card, where we're displaying the note above that we'll use our note component instead. So note, if we can make this a self-closing tag, move the closing tag onto a new line. Now we can just copy the VFR directive and the property, paste it into this note component. Now we can just get rid of this div with a class of card completely. On save that reload. We see an error here, cannot read properties of undefined reading content on our app is broken. And that's because in note dot view in our template, we're trying to access this note objects and this concept property from the objects. Well, this node object doesn't exist within the context of this note dot view components. We need to pass this down from view notes dot view, down to note dot view using a prop. 76. Props (Note): Let's pass down the notes objects, which we're using as the place holder in our V4 loop here. Down from view notes don't view down to note dot view. So that note dot Vue has access to note dot content. Don't see note component here. Lot of prop called notes and set that equal to note on save that. Now if we jump to note dot view, we need to receive that prop. We need to add scripts, setup tags, a lot of block comment, which says props. Now do you remember how we initialize our props? That's right. We create a constant called props. We set that equal to define props. We can pass our prompts in here, either us an array or an object. I'm going to use an object to it. We're going to receive a prop called note. The tight is going to be an object. We definitely need this prop in order for our component to render correctly. We should also add required to make this a required prop. Save that. And hopefully this should be working. Now, I'll reload yet Everything's work. And again, we can see our correct note content coming from the parent component array here. Let's just make sure we can still add a new note. We can still add a new note. 77. Computed (Note Length): Let's add a computed property which displays the number of characters that are in each note underneath the content of the note. First, let's set up the mockup. So endnote dot view underneath where we're spitting out the content, I'm going to add a div. And then inside that we're going to add a small tag. For now, I'll just put the texts XXX characters. Save that. We can see that on the page. To move this text over to the right, I'm going to add a class to the div of dash. Dash rights to give the text a light gray color, I'm going to add a class of text, dash gray Dash lights, save that. At least classes are from bola on the documented on that website. And just to add a bit of margin at the top, a lot of classes empty for margin top dash to save that, now we don't actually need a computed property to display the number of characters. We could just do double curly braces and then notes, dot content, dot length. Save that. We can see that works. However, I want to use a computed property somewhere in our app just to remind us how we set these up, Let's remove note dot content dot length. Instead, we'll create a computed property called character length, which displays the number of characters of the note here. Let's scroll down and sets up this computed property called character like. Now, do you remember how to do this? If you'd like, you can pause the video and see if you can have this on your own. Did you manage it? If not, don't worry. So to add a computed property, first of all, we need to import the computed method from view. So a lot of inputs, comment, I will import computed from view. And I'll jump to the bottom. I don't know the block comment which says character length sets up a computed property here. We want to call this character length, will create a constant called character length. We'll set that equal to the computed method, and then we'll pass another method into this method. Now all we need to do in here is returned something. We want to return the length of the concept property, which is in this note prop that we're passing down from view notes dot view. Remember when we were in script section to access a prop, we do need to use this props. Object. To access this note prop. We can just do prompts dot note. Then to access the concept property within that, we can just do dot content. We want to crop the length of that. We want to return this. So we can just add return to the stock. Save that, reload. This seems to be working. However, one thing you might notice is if we create a note but just has one character, then it still says one characters. It'd be better if it said one character. We can adapt our computed property to spit out this word as well. Based on the number of characters. I'm going to jump to our computed property. Before I do that, let me jump up to our template. Remove the word characters here and we now only see the number. Let's scroll back down back to our computed property before we return the value, I'm going to set up a variable called description. And we're going to set this to either character or characters depending on the length of the note. And we can use a ternary if statement to do this. So we can do let description equals if props dot, dot content dot length is greater than one, then we can use the word characters. Otherwise, we'll use the word character. Now we can just depend this description variable, either characters or character, the length that we're outputting here. So I'm going to return a template string here. Place this props.net.content dot length, dollar, curly braces like that, a lot of space and then allowed our description. So dollar curly braces and then description. Save that. That's not working. We can see 199 characters. One character, the note which only has one character. Now what duplicating a little bit of code, a prompts dot, dot content dot length. We could tidy this up a bit by putting it in a variable. So at the top here allowed, Let's, Let's call it length equals prop stop notes dot content, dot length. And then I'll replace props.net.content dot length, both here and here. I'll press Command D to select both of those. The shortcut might be different on your platform. Just replace that with these variable length. Save that, make sure it's still working. We see characters. If there's more than one character, character, it is only one character. 78. Delete Note (Emit): Let's allow a note to be deleted when there's delete button is clicked. Now that this note is in a child component, note dot Vue will need to emit an event from note dot view. When the button is clicked. Then listen out for that event on view notes dot view, and then fire another method here, which then removes the note from the notes array. Since our notes arrays in this pair of component or not in this child component. So first let's just trigger a local method endnote dot view. When this delete button is clicked, let's find the delete button in the template, and that's here. I'll split the attributes on that lot of click handler to that. I'll also use the prevent modifier to stop this link from behaving like a link and try to send those to a different page. Animal trigger a local method called handle, delete clicked. Let's create this method. Allowed a lot of block comment here. Handle deletes clicked will sets up this method. So const handle delete clicked equals an arrow function. For now I'll just log out handle delete, clicked, and save that. Click on the Delete button. On. Yeah, we can see that method is being fired. So now we can emit a custom event from this method. But first of all, we need to set up our emits array. Do you remember how we do that? A little bit like how we set up our props. You want to try it on your own, then feel free to pause the video. But anyway, I'm going to add a comment which says emits. To setup our limits, we can create a constant called emit, set it equal to the define emits method. We can pass all of our images in here in an array. So let's define an emit called delete clicked. Save that. We can now emit this event from this component. So back to our handle delete clips method. We can now just do emit parentheses and then delete clicked. Save that. Make sure we have no errors. Now we can listen out for this delete clicked event in the parent component view notes dot view. Let's jump to that component. Jump to our notes component here. I'm allowed custom event handler here. So delete clicked, which is the name we use for our remit when we receive that event, let's trigger a method in this component will trigger a method called delete note. Let's create that method. Download at the bottom. A lot of block comment which says Delete Note. We can set up this method, const delete notes equals arrow function. Let's logout delete note. For now, save that. Click on the Delete button. We can say this method in the parent component is being fired. However, at this point, how do we know which note to remove from the array? We need something to identify the note that we want to delete. We could use the ID property. If we jump back to note dot view, we could pass the note ID along with this image. We can just add a second parameter here. We can grab the ID from this note prop which we're passing down. This is going to be at props dot note, dot ID. In here. We can just do props dot notes on save that. Now let's jump back to the parent component denotes dot view down to this delete note method. We can pass this parameter in here. Let's call it ID to delete. Let's just log that out and make sure it's getting through. So ID to delete, save that. I'll reload, click on the Delete button. Yeah, we can see Delete Note Id1, which is the ID of the first note. If we click on the Delete button on the second note, you can say delete know id2. We know that this ID is getting through correctly. Now there's a number of ways we could remove the notes from the array. We could figure out the index of the notes based on the ID, and then delete the item which is at the index. If we want to use a one line solution that we can use filter to set this notes array itself, but filtered so that only the notes which don't have this ID are returned. What we can do here, we're going to overwrite this notes array ref. To access that href we can do not's dot value. That's overwrite it. We can just do equals. And we're gonna do not's dot value again. But this time we're going to filter it. So dot filter. First we need to add our placeholder, which will be used as we're looping through the array, as it's looking at each item in the array. So we can just use note for that. Then we can add an arrow function, which will be fired for each note in the array as we're looping through it. And then we can just specify what we want to return. I'll just stretch this out in a minute. Let's return only items in the array where notes dot ID is not equal to this ID that we're passing into this function. So not equal to id2, delete. I think that should do it. So let's save that. Reload. Click on Delete. And we can now delete our notes. 79. Pinia - Setup & State [Module 16]: I think now's a good time to add state management to our app using penny up. Generally speaking, if you're going to add state management to your app, the earlier the better. Because adding state management to an app, which is already really complicated, can bring a lot of headaches. But this time we're going to install a penny from scratch instead it all from scratch because we didn't choose to add it to our project automatically. Let's just Google penny. The penny a website. Get started and I'm going to jump to getting started. First of all, we need to install it with NPM or Yarn. I'm going to use npm. So we need to jump to our terminal. Kill the deaf process with Control C. Run npm install, penny, that's installed. So let's launch our app again with npm run dev, close the terminal. The next thing we need to do is create a pedia, the root store, and pass it to the app. We do this in main.js. I'll open up the Explorer and go to source and main.js. So first we need to import, create penny on from Pena. I'll copy this line and I'll paste it after this import create up from view line, paste that in there. And now we just need to trigger the US method on our app and then trigger this create penny, a method within that. After this creates outline, I'm just going to add dot use parentheses. And then within that I'm just going to create a parentheses. Let's save that, make sure we don't have any errors. Let's reload the app. There's no errors. So now we need to create a penny a stalk. I'm going to jump back to the penny a site. I'm going to jump backwards to what is penny a joke to basic example. And it gives us a basic example here. You can put your penny a store files wherever you want to. But they seem to recommend putting them in this stores folder. Let's create a new folder. The source folder called stores will create our STL file here. You can call this whatever you like, but I'm going to call it store notes, because this is a store for our notes and all related methods to those notes. Now I'm just going to copy the code of this basic example. Paste that in here. All this is basically doing is using this define store method to setup our store. And we put all of our state actions and getters in here. And then we just assigning that store to this constant and exporting it to make it available to any components in our app. So let's give this constant name, a more meaningful name. So we could call this US store notes. And also let's give this name here a more meaningful name we could call this store notes. For now, let's remove this action's object on these two comments. And we'll just focus on the state to begin with. Let's put this onto multiple lines. And now we can copy out notes or write into the state in the store. I'll close main.js and we'll jump to view notes. Dot view where we currently have on notes are right here. I'm just going to copy the two objects inside this array for now, copy those, jump. But to store Node.js will set a property called notes. Set that equal to an array. We'll just paste those notes into this notes array. Save that. We should now be able to import this store into any of our components where they can get access to this notes array, which is in our state in this store. Remember the state is only for storing data. 80. Use Our Store: Let's import this penniless though we've created into view notes dot view, notes page. And use the notes array that's in our store to display our notes on the page. Instead of using the notes array, which we've hard-coded into view notes dot view. First of all, we need to import our store into view notes dot view. We want to import this constant here, use store notes. So let's jump to view notes dot view. Look to our inputs and we'll just add import curly braces. Use stole notes from at slash stores, slash store knows, save that. Reload. To get access to the store in our component, we need to assign it to constant or variable. A lot. Another block comment here which says store. And we'll create a constant called store notes. Assign that to use store notes, parentheses. Save that. Now that we've done that, we should be able to see our store in our view dev tools. So let's click on this little double arrow and then view. It says in the console last donut store installed. And we can see how stole their free click on that. We can say our notes array all of the data from each note. Now let's output these notes from the notes array that's in our store. Instead of from the array that's hard-coded into view notes dot view here, we can just jump to our notes component. Instead of v for note in notes. We can do V4 notes in then the name of our store, which is store notes. We can do store notes on from that store we want to access the notes are right. So we can just do dot notes. Save that, reload. We can still see on notes on the page, except now they're coming from our store. Instead of this array, which is in our view note stop view component, we should now be able to just get rid of this note. So right, save that. We can still see our notes on the page. If we go to the DevTools, we change the content in one of our notes. We could say our notes update in real time on the template as well. I'll just reload that. If we jump to store notes, dot js, change the content of one of these notes. I'll just remove some of the texts and save it. I mentioned earlier on that hot reloading doesn't seem to be working at the moment. We didn't see that hot reload when I change the content of this note in the store. And I also showed you that there's a guide, the penny, a site. We scroll down here, module replacement, and we attempted to get this work in based on this example. It wouldn't work. I still haven't managed to find a way to get this work in at the time I'm recording this, but feel free to give it a try. Maybe it's working now in the future where you are. And if you do find a solution, please share it with this lecture. If I find a solution and a lot of lecture to this course explaining that. 81. Action - Add Note: Let's add an action to Penny, a store, which will allow us to add a new note to this note, which is in our state and our opinions store those, adding the new notes to the page, since these notes are now coming from these notes right in our state. Now, if we try to add a new notes right now, we see an error notes is not defined. And that's because if we go to view notes dot view on this Add note method on this line here, we tried to add a new note object to this notes array ref, which no longer exists here. But we now want to add a new note object to this notes array, which is in our opinion store. Let's jump back to the penny a site and jump to what is penny a basic example. It shows us here how we add actions to our store. We just need to add an object named actions after our state and we can place all of our actions in here. I remember actions are just methods which allow us to access the data in our state and then modify it. Let's jump back to store Node.js. And for now, I'll just collapse the state method and object by just clicking this little arrow. After this, we'll add an object called auctions. And we can now place all of our options in here. Let's create a new option called odd note. For now, I'm just going to log out our notes and say that we can now trigger this action from anywhere within our app. Let's trigger it from view notes dot view. We could just trigger this oxygen directly on new notes puts in here in this click handler. And we're pulling in a store as stole notes. So to access our store, we can just start store notes. And then to access the action we've just started notes. We can just start dot odd note. We don't need to add parentheses to this method when we triggering it in our template. So let's save that and see if that's working. I'll type in a note and click on new notes. Yeah, we can see I'd note being locked out. This is successfully firing our action. However, if we jump down to our previous ad note method, which is on view notes dot view. We have these two lines at the bottom, this one which is clearing out the new note ref, those clearing the text area. This line, which is focusing the text area by using this template REF that we've set so which we've added to the text area here. Now we're not going to have access to this V-model, declare it out, all this template ref to focus the text area from within our store. It makes sense to continue to trigger this local method, add notes, when we click on the Add New Note button and then trigger our action from here, so that we can still fire these two lines at the bottom, which are going to clear out the text area and focus the text area. So let's change that click handler on the art new notebooks inbox and what it was triggering this local notes method will comment out all of this code which was setting up the new node object. And then on shifting it to notes array ref, which we had here before. Then will trigger the action in our store. Instead, we can just do store notes. Note, when we're triggering an action from our script section, we do need to add parentheses. So let's save that and see if that action in our store is still being triggered. I'll reload, type in a note and click the button. And yet we can say, I'd know being displayed, well, this action needs to know what we should actually add to our notes right in the state. We need to actually pass the content of this note which has been typed into the text area to this action. So we can set up a new note objects based on his content and then add it to our notes array that's in the state. We could do this using parameters. 82. Action (with Parameters) - Add Note: We need to pass the new notes content from view notes dot view to our new add notes action which is in our store, before we can set up a new note objects and add it to this notes array that's in our state. We can pass parameters to actions as many parameters as we like, just like we can with any function in JavaScript. So let's jump back to View note stop view. And our new note content is in this new note href, which is bound to this text area. When we trigger our action here, we can just do store notes to add notes and then pass in new note dot value, and save that. Now if we jump back to our store notes dot js on this note oxygen, we can receive this parameter here. We can give it any name we want, so we could call it new content. Make sure this is getting through. Let's just log it out here. So when you note content, save that, reload. Click the button on. Yeah, that's working. We can see our action is logging out the note that we just typed. Now we just need to setup a new note object like this with an ID on a concept property and then add it to this notes already in our state. So let's jump back to view notes don't view. And let's call this code that we commented out. Save that. And we'll paste that into our action here. Remove the comments, fix the indentation, and we're going to need to modify this a bit. So this line is still fine. That's still going to get the current date on this line is fine. It's just going to convert that timestamp into a string and assign it to this ID variable. Let me set up on your notes. Objects were already setting up our ID. That's fine. But when we set the concept property, we no longer want to grab this from new notes dot value. We want to grab it from this parameter that we're passing in new notes content. Let's copy that, paste that here. And then when we unshift this new notes objects to our notes array, we now want to push it to notes array that's in our state. We can access the data properties in our state inside and oxygen using the this keyword. A bit like we did in the Options API to access our notes array, which is just called notes. We can just do this dot notes. And then we want to unshift this notes objects into this array. So we can just do this dot notes on shift, parentheses, note, and we can now just get rid of this line. And let's save that and see if this is working. Type in a new note. Click the button on. Yeah, it's working. We see it added to the page in our dev tools. We can say it's been added to our notes or write in our state here as well. 83. Action - Delete Note: Let's set up an action in our opinion store for deleting a node. And our delete button is in our note component. In components notes or note dot view. Let's say what's happening right now with this delete functionality. On this delete button here we have this click handler which is going to fire this local method handled delete clips. That method is here. And then inside that we're emitting a custom event called delete clips. I'm passing in the ID of the notes that we want to delete. Then in the parent component denotes dot view, the scroll up to the notes component. We're listening out for this event when we receive it, which are growing this delete note method. With that removing the note from the array based on this idea that we're passing in this local notes or eight no longer exists here. This is not going to work anyway at the moment. But anyway, once we start using state management in our app, whether it's through composable, UX or penny or like we're using. We don't need to worry about emitting custom events from child components to pair of components and then listening out for those events before we trigger a method. Because remember, all of the components of our app, no matter how deeply nested, have access to everything that's in our store, including everything in our state and all of our actions. Let's set up a new delete notes action in our store and trigger it directly in Node dot view without needing to emit any custom events or listen out for those events. Okay, so let's jump to store Node.js. And I'll just collapse this note auction at a comma and add a new action called delete notes. In order to delete the note from notes or write in our state, we are going to need the ID. So we'll set this up to receive that ID with a variable called ID to delete. We'll just log out ID to delete as well. Id to delete and save that. Now, note dot view. Instead of triggering this local method handled really clicked when there's delete button is clicked, let's import our Penny's door here. Trigger this new Action, Delete Note directly. Instead, I'm going to jump to view notes dot Vue. Copy this line where we're importing our store. Paste that at the top of note dot view that we need to get access to our store, like we did on the notes dot view by assigning our store to a constant. So I'm going to copy this comment on the code as well from view notes dot view. And we'll paste that after our prompts on the myths, we now have access to our store in this notes dot Vue component, we can now jump to this Delete button, trigger this Delete Note action directly. We'll get rid of this handle delete clicked method call, and instead we'll just do store notes, Dot Delete, Note. We need to pass in the note ID. Remember we're passing down the notes object with a prop here. So our note ID is available at props dot, dot ID. I'll just note dot ID if we're in the template so we can just pass in note DID save that. And hopefully our Action Delete Note should be receiving the ID and logging it's out. So let's see if that's working. Click on the Delete button. Let me see ID1 being locked out. Click on the other. What's enemy? See id2 being locked out. Now let's jump back to note dot view and do some tidying up. Because we no longer need this handled delete clips method. We're not using it anymore. We also no longer need to define delete clicked as an emit because we're no longer emitting that, we can just remove our emits code on the comment entirely. Save that. And if we jumped to view notes dot view, scroll up to the note component. We no longer need to listen out for this delay clicked event. We're no longer emitting it from note dot view. So we can remove them it as well. Save that. And if we scroll down here, we now no longer firing this delete note method. So I'm just going to copy the code from inside this method and then just delete this method on the comment as well. Save that. Now let's jump to store Node.js and we'll paste this code into this delete note method. We just need to adjust this a little bit. No longer gonna filter nope, stop value and then assign that filtered array back to notes stop value. We now want to do that with this notes already that's in our state. Again, we can just replace this Snopes dot value. I'll select both of these by holding down Alt. Instead, we can just do this dot notes. This is going to grab the entire notes are right on filter it based on the criteria in here. And it's gonna return all of the notes where the note's ID is not equal to the ID that we're passing in here. These variable names are still the same, so I don't think we need to make any more changes. Let's save that and we'll see if this is working. I'll reload, click on the delete button and we can see the note is deleted, and we also see the object removed in our dev tools as well. If I delete this one, we now see an empty array and our dev tools. 84. Edit Note Page & Route: We can now create, read, and delete our notes. We can't currently update our notes. Let's complete our crude set-up. Create, read update, delete by creating a new edit note page which will allow us to edit a note and save it. For this, we're gonna be adding a new action to our penny a store and also our first getter. But for now let's just set up a new route on page for editing a note. I'm going to jump to our explorer. In the views folder. I'm gonna create a new file called View Edit note, don't view. We'll add our template tags. To begin with. I'm just going to add a div with a class of edit, a dash notes inside that a lot of H1 tag which just says edit notes. Save that. And now we need to setup our routes. So let's jump to source router, index.js. Lot of new route here after the few notes route, I'm going to select all lot of press Alt Shift and down shortcut might be different for you to duplicate that. And we'll set the path to edit note in order to follow this route, to know which notes that we're going to actually edit load into the page, then we're going to need to pass a route parameter to this part. So to do that we can just dot slash and then colon, and then the name of a parameter, or we could just call it ID. So in other words, we could visit edit notes slash id one, and then we can grab the ID, ID one on this view and then use it to grab the appropriate note in our notes or write in our state in our store, and then display the concepts of this notes in a text area on this page so that we can then edit it and save it. And let's change the name of this route to edit notes, we need to import new component. So I'll duplicate this import few notes line and replace view notes with view edit note. And then I'll copy this name, view edit note and paste it in here. I'll save that. We should now be able to visit a path such as I did note slash ID one and get two new edit note page. Now we need to be able to get to this page without having to type in the address bar. Let's hook up these Edit buttons. I'm just going to close everything here except, nope, don't view. And let's find our edit button. And that's going to break this up onto new lines and then split the attributes on the opening tag. We can actually just change this into a router view component instead of an a tag. So let's rename the tag to router link. Make sure the closing tag is renamed as well. And we can now just start a prop to this to tell it where to go. I'm going to use a template string to do this using about six so that we can output the ID. We want to go to the path slash edit note, which we sets up in our router file, and then slash. And then we'll use dollar curly braces to output the ID. And again, we're passing the notes objects down to this node dot view components. Using these notes prop, we can get access to the ID out prompts dot note, DID, or in the template just note dot ID. In here. We can just put notes, dots ID, and let's save that. See if these Edit buttons are working. Click on the first edit button and yet wrote the path edit notes slash ID. Jump back, click on the second edit button. Now on the path edit note slash id2, we can access this route parameter which we've named in our router file here in our template by using the dollar route objects, which we could also use the options API. So let's just spit out this route parameter ID on the page and see if that's working. So we want to open up our new view edit notes component. So source views, view edit notes. And let's change this heading to edit notes with an ID of normal double curly braces. Access our route's information. We can add dollar route and then to access our routes parameters, we can do dot params and then to access ID parameter which we setup here, we can just do dot ID. So hopefully this will be spitting out on note's ID on the page. Now, let's save that. Reload. And yet, if we're at the path edit nope slash id2, we see id2 on the page. If we click on the first edit button, let me go to the path edit notes slash ID one. We now see ID1 on the page. 85. Reusable Component - AddEditNote: Okay, what we actually going to need on this new edit note view that we've created in order to edit a note and save it. Well, basically what we need is a text area which we can load the note into and allow the user to edit it. And we need a save button so the user can save it. So basically what we need is if we go back exactly what we have at the top of our notes view here, we just need a text area and a button. Now we could just jump to our notes view. In source views, view notes. We could just copy this card element and all of the fields inside it. Paste that into view, edit note dot view, and then repurpose it. Well then we'd be duplicating code. We always want to avoid duplicating code as much as possible. So it makes more sense to create a reusable component out of this card, the text area and the buttered that we can use anywhere within our app. Let's jump to our explorer in our notes folder. Let's create a new file called Edit note dot view. Because we're gonna be able to both add and edit a note using this component. Let's add our template tags. Now let's jump to view notes dot Vue will copy this mock-up, this div with a class of card. All the stuff inside it will copy that, paste it into our template here in our new component. Let's remove all of the dynamic stuff which will break here. So let's remove this V-model from the text area and remove this click handler on the disabled prop from the bullets hit. Save that. Now we want to be able to control the buttons that get displayed here. Because on our notes page we want this out, new notebooks in, but on the edit page then we probably want a button that says Save or Save note or something like that. We might also want to be able to add more than one button as well. So let's create a slot here where we can insert edit button that we want. I'm just going to get ableton. Instead we'll add a slot, will make this a named slots. So a lot of name, attribute and set that to buttons. Save that. Now let's use this new component on our notes view and get it all working there before we then use it on our new edit page. Let's jump to view notes dot view. I will import this component that we just created. That after our notes. I'll duplicate this line. And when you go to import, add, edit note from slash components slash notes slash add, edit notes, dot Vue. Save that. Now let's scroll up. I will comment out all of this card element. Then outputs are new component. Edit, note, save that. We see the text area, but we don't see the button. We need to pass the button into our slot. Inside this Add Edit note component, we can add a template tag. We can either add v dash slots, colon buttons, which is the name of the slot that we set up on Add Edit note dot view here. We can show this by just doing hash buttons and we can now place objects in it here. So let's grab that from the a mock-up that we commented out. So the button is here. I'll copy that. Paste it in here. Save that. Everything looks correctly now. But it's not working because this text area, which is now coming from our new reusable component is no longer this new note ref in our scripts section. Let's fix that next. 86. Hook up with modelValue: This text area on the button on no longer working. And that's because this text area, which is in our new child component at edit note dot Vue is no longer hooked up to this new note ref which we setup in this view notes component. Well, what we can do is pass this new note ref down to Edit note child component using V model. Then we can hook it all up using modal value and update modal value. So first let's set the V-model, this Add Edit note component to new note, which was originally bound to the text area. We can add a V model directive and set that to new notes and save that. And we can now receive the value of this V model in our edit note component using the special modal value prop, and then we combine the prop to this text area. Do you remember how we do that? Feel free to pause the video and give it a try. What we need to do is add our script setup tags. We need to receive the value of this V model using the model value prop. So we need to setup our props. We do that by adding a constant called props set that equal to the define props method. And we can pass our prompts in here. We want to receive the model value prop type is going to be string. This is gonna be required. So we'll set required to true. Save that. And we can now bind this modal value prop to the text area. We could do that by just setting the V-model to model volume. I'll save that. To check this is working. Let's jump to note that view I will change the default value of this new note ref that's here. So I'll stick some text in there, save that. We can say that text is getting through to the text area in our new reusable component. However, if we output this new notes rough somewhere on our page. So let's say we had a pre tag after this add edit nope component. And we just output a new note. Save that, reload. And then we make a change to the text area. We can say the value of this new note ref is not being updated. And so what we need to do it on new child component out edit note dot view is explicitly let the parent component know if you note stop view when the value in the text area has changed so that it can update the value. This new note href. We can do that using update model value. So let's jump to add edit note dot view. Do you remember how we do that? Again, feel free to pause and try and do it yourself. But what we need to do in this setup, our mitts create a constant called emit, set it equal to the define emits method. And we can pass in our imitable events into this defined emits array. And we want to add the events updates colon model value. This is a special event which will allow us to directly modify a value that's coming from the parent component by V model without having to emit any events from the child component and listen out for those events and then make the change. So update modal value allows us to modify this new note, href, which is on a its parent component. So let's jump back to it. Nope dot view. And what we can do here is listen out for the inputs event on this text area, which will be triggered every time we make a change to this text area. So we can just add up inputs. What we want to do is emit. With dollar emit. We want to emit the update colon model value event. And we need to pass in the value that we want to set that, to, set the value that's being passed down with the V model on the parent component as the second parameter. And so we can just set that to model volume. Save that. Let's see if that's working. Reload this page, change the value of the text area. Yeah, we can see this pre-talk on the view notes dot Vue component is being updated. So we know that this new note rough on the parent component is being successfully updated by the child component at edit note dot view by listening out for the input event on the text area and then emitting the update model value of passing in the latest value, which will then directly update the value of the V-model new notes, which we're passing in here. Before we move on, let's remove this pre-talk that we added on all of this code that we commented out. Get rid of that. We also need to set the default value of new note back to an empty string. Save that, reload. Let's make sure everything is working. So I put a new note, click on the button, and the new note was added. However, we see an error in our console cannot read properties of null reading focus. And that's because in this line here, after we add a new notes by sending the note constant to our app note action in our store, we're trying to focus an element which has a template href of new note ref. This href no longer exists on this component. This template Req is now down in the child component at edit note dot view here. That's why this text area is not focusing after we add a new note, and that's why we're seeing this error. Let's fix that next. 87. Fix the Focus: When we type in a new note, click the Add New Note button. We can see the notes is added to our list and add it to our opinions store to the notes are right in the state. However, the text area is not refocused and we see this error in the console cannot read properties of no reading focus unless because in view notes dot view after we add the notes to the notes are right in the store. We're using this template ref, new note ref to grab the text area element and then focus it. Well, this template ref, new note href no longer exists in this view notes component. So let's remove this constant here, which we setup for the template ref. And also remove this line where we were focusing the text area and find another way to do this. One thing we could do instead is add a template ref to the Add Edit note component that we're displaying here. And use that template ref to get access to the child component odd edit notes, and then fire a method that sets up within the Add Edit No.2 component. Let's add a ref to this component. And we'll call it add, edit, note, ref, save that and we'll jump down to the bottom. We need to settle a data, record this as well with the same name. So we'll create a constant called Edit note HREF, set that equal to a data ref with an initial value of null. Now in our note method here, after we clear the new note ref, those clearing the text area, we can then access the child component at edit note by using this template href, we can do edit note ref dot value to access that component. And we can then trigger a method which is in that component. So let's say we want to trigger a method called Focus text area. We can do this like that. Now we haven't set up this focus text area method yet. So let's save that and jump to add edit note dot view, setup this method. Let's start a new section here with a comment which says focus text area will create this method. So const, focus text area equals an arrow function. For now, I'll just logout focus text area. Save that. Let's see if this method in the child component focus text area is being successfully triggered by its parent component view notes dot view here. Let's reload. This method should be fired after we add a new note. So let's type in some texts. Click on the button. That's not worked and we see this error. Add, edit note ref dot value, dot focused text area is not a function. It's not able to find this function that we just sets up in the child component. And that's because when we're using the script setup pattern, we need to actually expose any methods that we want to be made available to a components parent component. We do this by using the define expose method and we pass into that an object. We just want to add in here any methods that we wanted to be made available to this components parent component, we want to make focus text area method available so we can just add focus text area here. So let's save that and see if that's working. Typing a note, hit the button. Yet that's working now we can see focus text area being locked out. So our parent component, do you notice dot view is successfully triggering this focus text area method on its child component to edit note dot view. So now all we need to do in order to focus this text area is add another template ref to this text area and then use that to focus it here, Let's scroll up to the text area element. In this text area still has a roof on it from before this new note ref well, this text area is not just for new notes anymore. It's also going to be useful editing notes. Let's rename this to text area href, which is a bit more generic, and we'll use this href to focus the text area. Let's jump down to our focus text area method. We also need to set up a data ref for this template rough with the same name. So we want to do const text area HREF, set that equal to a rough with an initial value of null. Let's just check if we're importing the ref method from view and we're not. Let's just add an input section at the top. I will import from view, jump back down to our method. Now to get access to the text area element, we can just do text area ref dot value. And then to focus it, we can just fire the focus method. So focus, say that and hopefully this should be working now, type in and you note, hit the button on. Yeah, it's worked. We've added the new note. We can see it on the page and we've also clear the text area, unfocused it as well. 88. Custom Color, Placeholder & Label Props: Let's add new reusable component at edit nope dot view to our new edit note page. I'm going to jump to view notes where we are currently using the Add Edit note component. And I'll just copy this close that jumped to the new view, view edit nope dot view. And we'll paste that inside this div. And let's just remove all of the dynamic content for now. So I'll remove the V-model, remove the click handler or the disabled attribute, save that. Now we need to import this component before we can use it. Let's add our script setup tags. Add our inputs section, and we'll input edit, note from slash components, slash notes slash add, edit, note, dot Vue, save that. We can now see that component on the page. I'm seeing a warning in the console here. I'm missing required prop model value. And that's because this component to edit note dot view is expecting the modal value prop or the V-model prop, and this is set to a required prop. Let's set up a rough on Q edit note dot view that we can bind to this edit note component in our scripts section, a lot of new comment which says Note. We'll setup a ref called note content. Set that equal to a href with an initial value of an empty string, because we're using the ref method, we need to import this. So let's import ref from view. And now we can bind this raft to our Add Edit note component. We can now add V-model. Remember all we need to do is add the V model because we've already sets up this ad edit note component to automatically pull in whatever V model we pass into this component using the modal value prop, and automatically update the V-model, which is on the pair of component by using the update model value events. So all we need to do now to hopefully soap is just bind this note concept ref to this component. So we'll set this V-model to note content and save that. And let's just make sure that's hooking up. I'll put some text in here. We can see the update on the text area. Let's set that back to an empty string and save that. Let's change the text on this button here will change this text to say note, save that. Now I would like this card which is surrounding our text area in button to have a different color when we're on the edit page. So it looks a little bit different from the card text area and button which is on the notes page. If we open up our reusable component out edit note dot view, we can see this class on the card has background success. We can change this word success to create different colors. For example, we can change this to has background link dark to give it this blue color, Let's set up a prop on this Add Edit note dot view components so that we can dynamically change this word here, those changing the color. I'll set this back to success for now and save that. Now let's jump down to our props and we'll setup a new prop called BG color. We'll set the type to string. I will get this a default value as well. So that if we don't provide this prop on a parent component, we can still have a background color on this card will set the default to success. I'll save that. And now let's jump up to this div with the class of card. I'll split the attributes on that and I'm going to cut this house background success, dark glass. I will bind to the class attribute. I'll use a template string here using backticks so that we can output the concept of this prop or paste in that class that we copied has background success, dark. Let's just get rid of the word success and our dollar curly braces and output our prop instead BG color. We can either do props dot BG color, or we can just do VG color. I'll save that reload. And we can see that if we don't provide this BG color prop to this Add Edit no component when we use it on a pair of component, then we see the default background color because it's going to use this default value in the prop which we set here. But we should be able to override this word success by providing this prop to the Add Edit note component when we use it in a parent component. So let's jump to view edit note dot view. I'm a lot this prop to our Add Edit. No component here will set BG color, and we'll set this to link, save that. And we now see this blue 11. And if we inspect it, we can see that the card, how's this class has background link doc. But if we go to the notes page and inspect that card, we can see the class has background success, dark, background color is working. Now, let's also make us save notes, but it's in the same kind of color as well. The saved notebook and he's here. So let's change that class too, has background link instead of success. Save that. And we now have a blue button while we're at it. Let's disable this button when this field is empty. Again without the disabled attribute conditionally only when no content is empty. So we'll set this to not note content. Save that. That button is disabled by default, but if we type something in and it becomes enabled again, now we also need to make this placeholder text in the text area customizable as well because the text add a new note doesn't make sense on this page. Because this text area is gonna be for editing a note or not adding a new note. Let's add another prop, add edit note dot view for this placeholder text, which is currently just hard-coded onto the text area, will set up a new prop called placeholder. Settle out to a type of string. We'll give this a default value of let's say type something, dot, dot, dot, save that. Now let's use this prop on our text area. We can just bind to the placeholder attribute and then output this prop, either with props dot placeholder or just placeholder. I'll save that reload. We can now see the default value of types of thing in the placeholder, but we should now be able to override this by passing in this placeholder prop to our art edit note component when we use it on one of our pages, Let's jump to view, edit nope, dot view. And we'll add this placeholder prop, placeholder. We could set this to edit, note, save that, and we say that in the text area. Now if we jump back to our notes page is still says type some things. Let's override that as well. So we'll open up View notes dot Vue will have this placeholder prop here as well to our art edit note component. So placeholder, almost at least to add a new note, save that and we see that update. I'd also like to be able to display a label at the top of our text area to make the function of this text area a bit clearer. First, let's just set up the markup for this. So I'll jump to add edit app.vue. And at the top of our card element above this field, I'm going to add a label with a class of label. For now, I'll remove this for attribute. I'll just set the text to labeled texts for now. Save that. Let's make this white. So a lot of class of texts white to make it white. And again, these classes are just from Bohmer and I want this label to be optional. So let's settle a, another prop for this. We'll set up a prop called label onset not to a type of string. And save that. And I only want to display this label element on the page if this prop has been provided. So I'm going to split the attributes on this, or we can just add a v-if directive, and we only want to display this if the label prop has been provided, you can either do v if prompts dot labeled or just vf label. So I'll save that and we see that labeled disappear since we're not actually providing this labeled prop to this Add Edit no component when we use it on our edit page here, what if we add this labeled prop here, label? We could set this to edit notes, save that, and reload. We can now see the label again, since we're providing this prop, although it's not displaying the value that we're passing in here, Let's jump back to Add Edit note dot view, and we can just output the contents of this prop here. We can just replace labeled texts with double curly braces and labeled save that. And we now see edit notes. And if we jump back to the notes view and we don't see that label, since we're not providing that labeled prop on the view notes page here. The reason that I think we need a labeled here is because when the user clicks on edit notes, they're not going to see an empty text area here. They're gonna see this text area populated with the note. And so they won't see that placeholder. So this will just give the user a little bit more clarification before we move on and start adding and you get to our opinion store for getting the content of the note that the user is trying to edit. Add an action for updating the note. Let's just add a cancel button here, which takes us back to the notes page. Since right now there's no way for the user to get back aside from clicking on the blackboard. So what you want to view edit note dot view on inside our buttons slot here, we'll just add another button. So I'll duplicate this saved note. Remove the disabled, prop changed the text to counsel. Save that out. I'm gonna change this class has background linked to is dash liked to give it this light gray color. Now there's a couple of ways that we could send the user back to the notes page when they click on this, one thing we could do is convert this button into a router link component, set the two prop to slash, save that and see if that works. That works. The other thing we could do is leave this as a button and add a click handler to this click. And we could use the dollar router method, because we can still use the dollar router method in our templates using the composition API. So we can either do dollar router dot, push, forward slash, save, that works. Yeah, that works. Or if our app was more complicated and had lots of different ways that we could get to this edit page from different pages that we might want to just send the user straight back to wherever they came from. So to do that, we could just do dollar router dot parentheses, save that reload. That works as well. 89. Getter - Get Note Content (useRoute): When the user clicks on this edit button that's taken to this new edit note page. And they're going to expect the text of the note that they just clicked on this text here to be loaded into this text area. And we can do this by using a getter in our opinions store. What we're going to need to do, grab the ID of the current note from our route parameters, which we can see in our address bar here, ID one, and then send the ID to a getter in our store, which will then retrieve the correct note from my notes are right in our store based on the ID. Return it to this view edit note component, where we can then assign it to this notes content href, which is bound to the text area, those populating the text area with the texts from the correct note. To begin with, let's just sets up a really simple getter, which just gets the content of the first note in our array. Let's jump to our store file store Node.js. In the stores folder. We'll scroll down to add some guesses. We just need to add a getters objects. After this action's object. We can place all of our getters in here. It gets out. We just started a property with the name of the ghetto w1, or we could call it get note content. And then we assign this property to a method like this so that this gets, I can get access to US state, we need to pass the state into this ghetto like this. Getters always need to return something just like computed properties. So for now let's just return the content from the first note in notes array in our state. So we can grab this from state dot notes and then square brackets 0 to grab the first note dot content, Let's get the content of that first node. In our guts. All we need to do is return state dot notes, square brackets 0 to grab the first one, dot content and save that. We should now be able to use this getter anywhere in our app. So let's use it on View, Edit note dot view. We need to import our store. So we need to do import US store notes from slash stores slash store notes. That we need to assign these two constants so that we can access it. A lot of new comment which says store. I will setup a constant called store notes. Set that equal to use store notes parentheses. And actually let's move this up a bit. I'm gonna cut that, paste it after our imports. Now we can use the getter in our store to grab that concept from the first note and assign it to this note content ref, which showed that update the text area. To access our ghetto. We can just do store notes dot and then the name of our getter is get note content. We just want to assign this to this note content ref, so we can just do note content dot value equals like that. So let's save that now, reload and that doesn't seem to be working. Let's have a look in our console, not seeing any errors. Let's have a look at our store file. Just make sure the app is running. It's running it help if we were actually on the edit page. So let's click on an Edit button. We can see the texts from the first note is being loaded into this text area. However, we don't just want to grab the text from the first note array. We need to be able to grab the content from the notes based on the ID of the note that they just clicked on. If we click on Edit on the second node, then we expect to see this notes content being loaded into here. We can grab the ID that's being passed to this page using our route parameters we saw earlier on that we can split this out in our template using the dollar route object. After this, add edit notes, I'll just add a pre tag, double curly braces and then dollar route dot params. And then the name of our parameter ID. We need to pass this ID to our getter so that that gets, I can get the content for the correct note. However, we need to do this in our script section. And we can access our route information in the script section using the dollar routes objects. When we're using the composition API, we need to use Vue routes as new US routes composable. So let's just remove this pre-talk. After this import here we'll import US route from view dash router. And then a lot of comment here which just says router. We need to assign this user out composable to constant or variable. So we'll set up a constant called routes. Set that equal to use routes parentheses. And we can now access the same routes information which we just split out before using this route constant. Let's just see if we can log out this route parameter, this ID parameter f. Do we setup this note concept href. We'll just logout console.log, route dot params dot ID, save that. And yet we can see id2 is being locked out. And if we go back to the notes page, I'll click on Edit on the first note and we see ID1 being locked out. How can we pass the value of this ID to getter? 90. Getter (with Parameters) - Get Note Content: How can we pass this ID to our getter? You might think that we could just start parentheses to r gets a here and pass it in like this, routes dot params dot ID, save that, then jump to our store file, down to r, gets up and maybe past this parameter in as a second parameter. We can see this doesn't work. We see an error in the console. Stole notes don't get no content is not a function. That's because this getter is not a function, it's just an object property thus assigned to a function. What we can get around this by making out gets a return, a function with the parameter that we're passing through. Let's get rid of this ID parameter and get rid of this return line. What we can do is just return an arrow function like this. Pass in the parameter here. So let's see if we can lock this out now. Console dot log lot of string ID from Geta colon and then will split out this ID parameter. So I'll save that reload. We can see that's working ID from getter ID1. We see a warning here, but don't worry about that. That's just because getter is returning a function at the moment or not a string. We're assigning that to this note content href, which is being passed to add edit note in the V model, the model value prop on this component is expecting a string. So we'll see this disappear when we get our get to work here. Let's jump back to store Node.js. And now we can use this ID to grab the correct note on this content property from the notes array that's in our state. So we can use filter to do this. Inside this method here, we're going to return notes are right, so state dot notes. But then we're going to filter it with the filter method. Let's stretch this out a bit. This is going to loop through each item in our array. We're going to need a placeholder for each item as it's going through. So we'll use note for that. Then we add another arrow. Folks should specify our criteria for the notes that we want to grab. And we want to crop the notes where the ID property is equal to the ID property that we're passing in here from our edit note page, we want to return notes where the ID is equal to ID. Since all of our IDs are gonna be unique, then this will return an array containing just denotes object that we're looking for. Since this is gonna be an array, we need to grab the first and only item from the array by adding square brackets 0. And then from that we want to drop the concept property so we can just do dot content. Let's save that and see if this is working. Reload the page. I didn't note slash id one. And we are seeing the concept from the notes with an ID of ID one. And if we jump back to the notes page, click on Edit on the second note, then we see the content from the second note with id2 being loaded into the text area. 91. Action - Update Note: When the user makes a change to this note and they click on Save, we want to update the concept property of the correct note in our state with the new content that they've typed in on. First of all, let's just trigger a local method. When we click on the Save button, I'm going to jump to view edit note dot Vue, going to remove this console log here. Let's jump up to our saved notebooks in animal cell. Going to add a bit of margin between these buttons is actually on the console button allowed. Three, let's say maybe two. And it looks a bit better. Now let's add a click handler to this saved notebooks in. So click equals will trigger a method called handle save clicked. And let's go and create that method. Lot of new comment here which says Save clicked, sets up our method handle save clicked sub I equal to an arrow function. Just log out. Handle save, clicked for now, save that, click on the button. We can see that's being locked out. We're going to trigger an action in our store at this point to update the notes and notes are right in the state. What information is that action going to need in order to do that? Well, it's going to need the note's ID so that we can figure out which notes in the array to actually update. And it's also going to need the new content string as well. So there's a couple of ways we could pass this information to an action in our store. So we could set up an object. We could call it payload or something like that. We could add a couple of properties to this ID, which we can again get from our route promises. So routes dot params dot ID, and then a concept property which we can just grab from this note content href, which is bound to the text area. So note content dot value. And then we could just pass this whole payload as a single parameter to an action packed with UX. This was the only way that we could do this because actions in UX can only accept one parameter. However, elections in Kenya can actually accept multiple parameters. So the other way to do this is just to send the ID on the content as two separate parameters. In this case, I think we should do it that way because it will make our action a bit more clear because we'll be able to say exactly what's being passed in here. So let's set up this option will create a new action called Book date. Note, this is going to receive an ID parameter, the content parameter r. Let's just lock these out. I'll Logout ID. Then we'll log out the concept parameter as well. Save that. Now let's trigger the oxygen here and our action is gonna be at stole notes, dots, update, note, because this is the name we gave our action here. And this is going to expect an ID parameter on the concept parameter. So the ID we're going to drop from our route parameters, routes stop Ram's dot ID. And then the concept we're just going to grab from this note content href, which is bound to the text area. So we can get that from note content dot value. Now let's save that and see if these two parameters are getting through to this action. Reload, change the content, click on Save. And yet we can see those being locked out by our action. You can see the id being locked out on the updated content being locked out as well. Now we just need to update the correct notes in our state. And one way we could do that is by first figuring out the index or the position of the notes in our array by using the find index method. And then we can just simply update the notes in our right at that position and update the content property. First of all, let's just grab the index of the notes that we want to update. So I'll set up a variable called index. And then we're going to fire the bind index method on our notes are right. And we can get to our notes array at this dot notes. So we can do this dot notes, dot find, index. And again, we'll use notes as our placeholder as it's looping through our array. Set that equal to an arrow function. And we want to return the index of the note where the note's ID is equal to this idea that we're passing to this action equal to Id. And actually I think we can just shot him this by removing these curly braces and the word return. Let's just see if that works. Also, I'll do the same to this filter down here, can show this by removing the parentheses, return on this closing parentheses, since we're only firing one line here. So let's just log out this index and see if that's working. Index. Save that, reload the page and change it and click on Save. We can see the index is, is set to one here, which is correct because that's our second note. If we jump back to the first note, make a change and click Save. Let me see index 0 being logged out. Okay, so now that we have the index of the notes that we want to update and our notes array, we can now update the note. So let's scroll down to our update action. I'm gonna get rid of these console.logs, access notes, array. We can just do this dot notes. And then to access the notes at the position of index, we can just do square brackets index. Then we want to access the concept property and set it to this content that we're passing through to this action. So set that equal to content and remove this console log. Let's see if that's working. So I'll reload, make a change new concept. Click on Save. Now if we click on cancel, we can see that the first note has been updated with the new content. Let's make sure it works on the second note as well, click on edit out some new content, click on Save, click on cancel. This node has been updated as well. However, when the user does click Save on this button, we don't want to leave them on this page. We really want to redirect them back to the notes page. Let's do that next. But before we move on, I've just realized there's another return statement here that we can shorthand. I'm going to remove this curly brace in our delete note action on return. And I don't remove the closing curly brace as well. Let's have a look at the odd note. That one is okay as it is. Let's save that. 92. useRouter - Redirect to Notes Page: Let's redirect the user back to the notes view. After this note has been saved. Let's jump to view edit note dot view. We need to do this in our script section. After this line. In the Options API, we could just do this dot dollar router. Then don't push and push them to the path slash. While in the composition API, this is not gonna work. So let's get rid of that. And we need to import the US router composable from Vue Router. Let's jump up to the top on here where we're importing use route. We can just start use router that we need to assign this a constant before we can use it. So we can do that after we've setup, I'll use route constant. We can just do a const, use. Const routes equals use router. We can now access all of the usual route. So methods such as Porsche and replace back using this constant. So now down to handle saved clicked method. After we update the note, we can just do router dot push, will push them back to the notes page, which is at the path slash. Let's save that and see if that's working. Reload the page, changed, the content, can save on yet we're back on the list of notes. We can say that a node has been updated. 93. More Getters & Stats Page: To further demonstrate that our opinions store, store Node.js can be used anywhere within our app. Let's create a new page, a Stats page, which actually we've already sets up a page on a route for that. On this page, let's display a table which displays some information about our notes. We're going to display the total number of nodes that we have and also the total number of characters of all of our notes combined. But before we set up this Stats page, I've noticed that this mobile menu is not disappearing when we click to go to a new page. Let's just quickly fix up first Enough bar is it a source components layout and Navbar? Let's scroll down to the script section. This show mobile enough ref is what is determining whether or not the mobile menu is shown. If I change this to true, we can say that the mobile menu is shown by default. I'll set that back to false. And so what we could do is just make sure we set this back to false whenever one of our links in the mobile menu is clicked. So let's jump up to A2 router links here, notes, and stats. And we can just add a click handler to both of these. I'm gonna do a multiple selection here. Put my cursor here next to the word routes link. Hold down the Alt key and then put another cursor next to this router link opening tag, hit Enter. I'm allowed to click handler and we're just going to set this show mobile nav back to false. So we can just do show mobile now equals false, save that reload and hopefully this mobile menu will disappear when we change pages, and it does. So let's use Ballmer's table component to display some data about our notes. We'll just set up the markup for that first, I'll close these files module to ask dots, page views, view, stats, dot view. To add a table with some nice styles, we just need to add a normal table with a class of table. Let's create a table with a class of table within that without T head element on our body element. And then in our T head element without a row. So TR, table row. I'm within that. We'll add a couple of Th cells, table heading cells. In the first one, I'll just put the heading stopped, duplicate that. And then in the second table heading, I'll just pull value, save that. And we can now see our table header row that. Then in our body we'll add a table row on inside that will add a couple of TDS table data cells. In the first one we want to put the name of our stats. So I'm going to put number of notes. And then in the second cell we're going to display the actual number of notes, but for now we'll just put XXX and I'll duplicate this table row and then follow the name. I'll put number of characters, parenthesis of all notes. Say that to make this table full width, we can just add a class to the table of is dashed full dash width. This is just another former class, say, but actually I think full width is one word. So let's get rid of that dash, save that. And we now have a pretty decent looking table where we can display these stats. So first let's set up a getter for displaying the number of notes here. So let's jump to our store file source stores stole notes dot js, jump to our getters at a comma and add a new Get up. So we could call this total notes count that equal to an arrow function, pass in the state. We want to return the total number of notes in our notes right in our state. So to access notes array we can do state dot notes. So we can just return state dot notes. And then to get the number of items in the array or the length of the array, we can just do dot length. Save that. Let's jump back to View Stats dot view. Now we need to import our store. So let's set our script setup tags at a comment which says inputs. And we're going to import US store notes from slash store slash store notes that we need to assign this to a constant. Great, another comment here under start const, store notes equals use store notes. We should now be able to use our getter. So let's replace this first XXX after number of notes with double curly braces and store notes. What did we call it? Total notes count. So still Notes dot total notes count. Save that, and we now see that update to two. And we do have two notes. If we add another note, maybe a couple more. Jump back to the stats page. This now says For, you can see that we can easily use everything that's in our store, anywhere within our app and all of the data in our store and all of the results of our getters are always kept up to date in real-time regardless of where we go within our app. Now that sets up one more getter for grabbing the number of characters of all notes. I'm not sure if the number of characters just change that. Let's jump to our store again, add a new getter, and we'll call this one total characters count at an arrow function. Pass in the state. Now we're going to have to loop through every note that's in our notes array in order to figure out the total number of characters of all notes. We could just set up a variable for counting this. I'll create a variable called count set that equal to 0 by default. And then we'll just loop through our notes, right, the length of the content from each one, and then add that length to this count variable and then just return this count variable at the end. So we could use a for each to do this. To grab our notes array, we can just do state dot notes that we can use a for each method to loop through each note in this array as it's looping through and it's looking at each one, we're going to need a placeholder. So we'll just use note for that, then add an arrow function. This is going to look at the first node and assign it to this note place holder. So we can just drop the content, nope dot content, dot concept. And then to get the length of that string, we can just do dot length. We can then just add the result of this to this count variable. We can just do count plus equals like that. And then when it's done, looking at the first item, it'll jump to the second item and then grab the length from next concept property and just keep adding to this count variable until it's gone completely through the array. Then at the end we can just return this count variable. So let's just return counts, save that, reload the page. Or we need to actually use this getter, which we've called total characters count. Just going to copy that name. Jump to view, start-start view, select this XXX double curly braces. And then to use our ghetto, we can just do store loads, dots, total characters count, save that. Now we see that update. If we jump to the notes page, edit one of our notes. Jump back. We could say this value is now increased. If we delete all of our notes, jump to the Stats page. We now see we have 0 nodes on 0 total characters. 94. Directive - Autofocus [Module 17]: Let's add a custom directive to this up. One thing we could do is create a custom directive which auto focuses this text area when the page is loaded, that the user can immediately start typing and you notes without having to click into it. First of all, let's just settle a local custom directive inside this Add Edit note component. I'm not saying source components, notes and edit notes will jump down to the script section. I'll jump down to the bottom. Let's add a new block comment here which says directives. Do you remember how we add a Local Coastal directive? Feel free to pause the video, see if you can remember how to do it. But what we do is we create a constant and we need to make sure the name of this constant starts with a lowercase v and is in CamelCase. So we could call this vein autofocus. We set this equal to an object and we could add any hooks we want in here such as created, we're going to use mounted and we set this equal to a method like this, and we can pass the L parameter in here. This l parameter will give us access to the element once it's mounted as a in the element which we add this V autofocus directive to. We can then just focus it with L dot focus. Now we can just add this directive to any focusable element that's in this component. So let's add it to our text area which is here. Remember we need to change the name that we've used here from camelCase into dash case. We cannot this like this v dash autofocus. Save that. And if we reload this page, we can see that the text area is automatically focused without us needing to click into it. Since this art edit notes component is a shared component which is also used on the edit page. It should also work here as well, which it does. We can see the text area auto focused here as well. 95. Global Directive - Autofocus: Now let's say we have an input on us Stats page. Let's jump to the stats Page, Source, Views, view, stats dot view. And I'll collapse our table and we'll add an input after this. And I'll set the type to texts and we'll give this a placeholder. Do you love? Nope balls. Save that, and jump to our stats page to make this look a bit prettier. A lot of class of inputs, which is from Bohmer. That's better. Let's say we also want to autofocus this impulse when this page has loaded. Well that's not currently going to work. If we add this be autofocus directive here, save that and reload. If we look at our console, we can see an error. Cannot read properties of undefined reading deep. That's because this V autofocus doesn't exist within the context of this component view stats dot view only exists on the art edit notes dot Vue component, where we've set it up here. Let's jump back to view stats dot view. Remove this V autofocus directive, save that, and jump back to our notes page and add edit nope dot view. And let's make this directive a global directive that we can use anywhere. So first of all, let's create a new folder in our source folder where all of our directives can live. So I'm gonna right-click the source folder, click on New Folder, and create a folder called directives. Inside that we're going to create a new file called autofocus dot js. We might as well give it the same name as our constant name. Now let's jump to add edit nope dot view, select this constant and cut it. And let's also remove this comment and save that. Now let's jump to a new V autofocus file and paste up directive in all we need to do is export this, that it can be imported from our components. So we can just start exports by that, save that. And now if we jump to add edit self.view, we can just import this directive. So let's jump up to our inputs and we can just do import curly braces V autofocus from slash directives slash the auto focus. Save that. Now we just need to add the directive to the text area, which we already have done. So this should hopefully be working now and we can see when we reload the page is still autofocus is the text area except now it's using this global directive that we've created. So let's use the same global directive on our stats page. Let's jump to view stats dot view. We need to import the directive. So I'll just jump back to Add Edit note dot Vue. Copy the import statement, jump to view stats dot view, paste that here, save that, and we can now use that here as well. So let's add it to this input we just created. So V dash also focus, save that, reload and we can see this input on the start page is now autofocus as well. 96. Watch the Number of Characters (Watch): Let's add a watcher to this up. Let's say on our notes page view, notes dot view, we want to watch the number of characters that are being entered into this text area. Or in other words, the number of characters in this new note ref that's on view notes dot view. And if that gets to a 100 characters, we want to show an ellipse. Let's add a watch her to do this on view notes dot view. So do you remember how we add a watcher? That's right. We first need to import the watch method from view, and we then use this watch method. At the bottom here, I'm going to add a new comment which says watch. Characters will use this watch method. The first parameter is the data that we want to watch, and we want to watch this new note, href, we can just pass in new notes as the first parameter. The second parameter is a method callback method which will be fired anytime this data property that's specified here changes, if we like, we can pass in the new value on the old value here. Let's just lock these out. So a logout a new value and then comma new value. Then I'll duplicate that and we'll logout old value. Save that, reload. If we change the texts, we can see the old value and the new value always being locked out. Now we're not actually going to need the old value here. I'll get rid of that and get rid of these logs. If the character length of this new note ref gets to 100, we're going to show an ellipse. We can just add an if statement to do this. So we can do if you value dot length to get the length of the new value is equal to 100, then we can alert somebody, lit only 100 characters allowed. Gosh, darn it. If I reload. And just to make it easier to test this, I'm going to jump to the underlying text area, which is in source components notes on odd edit note dot view. I'm just going to add a max length attribute to this set that to 100. Let's test this out. We can just start typing into this text area. When we get to 100 characters, we see the alerts. We can say that our watcher is working. 97. Composable - useWatchCharacters: Now let's say we want to have the same watch characters functionality on our stats page on this input. Well, we could just copy this watch code, paste it into Stats page view, stats dot view, and then just adjust the data property that we're watching here. Well then we'd be duplicating code. It'd be bad to put this watcher into its own composable file, which we can use everywhere. So let's do that. Let's jump to our explorer. And we want to put our composable in a folder called use inside the source folder. Let's right-click source and choose New Folder. Create a folder called use that inside that will create a new file called US watch characters ab.js. To set up a composable, we just need to export a function with the same name as our file. Use watch characters. Like this. Let's jump back to view notes dot Vue. Just caught this watcher. Jump to our composable and paste that in here. I'm actually backhaul Snopes dot view. We can now remove the watch method that we're importing. Save that, jump back to use watch characters. And we do need to import this watch method here. Let's just add inputs, watch from view. Now in order to make this composable generic, we need to be able to pass in the data property that we want to watch as a parameter. So let's just accept that here. So we could just call this value to watch or something like that. Whatever we're passing in here is what we want to watch here, Let's change new notes to value to watch and save that. And now we should be able to use this impossible to watch the number of characters of any data property within our app. And then show this alert if it hits 100 characters. So let's jump to view notes dot view, and we need to import this composable. So inputs use watch characters. From slash users slash, USE watch characters. Say that, let's jump to our notes page. Jump down to the bottom. We should not be able to use this composable to watch this new note href, which is bound to the text area. We can just do use watch characters and then pass in the rack that we want to watch. New note, note, save that. Let's see if that's still working. Still working. And now we should also be able to use the same functionality on our stats page. So let's jump to our stats page. Views, view, Stop, Stop view. Again, we need to import this composable. I'll just copy the input line from view. Note stop View. Paste that here. And we should now be able to use it on the stats page as well. However, we don't have a data property setup for this input here. So let's just set that up. A lot of comment here, which says love note balls almost sets up a href. So const, love note balls equals a href with an initial value of an empty string, and we need to import ref from view. Let's add that to the top input ref from view. Now let's bind this raft to the input. Here's the input. V-model. Set it too low of note balls. Save that. Let's just change this default value to see if it's hooked up. Yet. That's all it took. We should now be able to watch the characters in this input. So again, we can just do use watch characters passing the wreck that we want to watch, which is love, nope, balls, love, note balls. Saint that. Hopefully we should see the same functionality here. Yeah, it's working. 98. Composable - Multiple Parameters: Let's make our use watch characters composable a bit more flexible. So right now the alert will always be fired when the length of the characters reaches a 100. But let's say we want to be able to adjust the number of characters at which this phi is. Let's say on the notes page we want to find this out 100 characters. But on the stats page in this input, we want to find this up 50 characters. Well, we could do this by adding another parameter to use watch characters root function here. Let's add a second parameter here called max jars. We can pass the same when we use this composable in our components in our if statement here, instead of if newValue liked is equal to a 100 will do if new value dot length is equal to max chars, I'll replace a 100 width max chars, and we'll also output this number in our alert as well. I'll change this to a template string. I'll replace the $100 curly braces I'm allowed put this max chars. Let's get this much jars parameters set a default value in case it's not been provided. We could do that by just setting max chars equals to 100. Save that. Since we're not currently providing this max child's parameter on the notes page, then this should just work as it did before. By firing the alerts are a 100 characters. Let's just test that. Yet not still working at a 100 characters. Let's say on our notes page, we just want to use the default functionality with a 100 characters. But on our stats page we want the max chars to be 50 characters. Let's don't see the stats page on jolt to view stats dot view. Let's scroll down to where we are triggering our composable, passing the second parameter of 50. Now let's save that. Reload. This should be firing a little bit sooner now at 50 characters, it says only 50 characters allowed. Gosh darn it. And so our composable is now a lot more flexible and useful. And of course we could make it more flexible with additional parameters, etc. Now for a simple composable like this, which only contains a watcher, then it makes sense to just import the composable and just fire the root composable function like we're doing here. But for more complicated, composable, which contain a bunch of different things such as data, methods, computed properties, etc. Then it makes more sense to only extract the things that we need from the composable using the structuring. And for a reminder of how we do that with more complex composable, jump back to Module 11. The composable module. 99. Click Outside Composable (VueUse, Template Refs): We've created a custom composable, but let's also add a composable from the view library. And if we look at our mobile menu here, currently the only way we can close this is by clicking on the x. We can't close it by clicking outside of it, like we would usually expect. So let's use one of the composable from the view use library to solve this forest. Let's jump over to view use.org. Get started. We need to install this first. Let's talk to our terminal. Kill. The death process will run this command to install Vue use. I'm not finished, so let's relaunch our app with npm run dev. On the view side, we're gonna go to menu functions, senses. We're going to use this onclick outside composable. So let's click on that. We can see an example here. We can open up a modal. We can close it with the X button, but we can also close it by clicking anywhere outside of the modal. So let's hide the terminal here. Close all of our components. Open up the navbar source components layout, Napa. Let's look at the example code here. We need to import the onclick outside composable from view use. So let's copy that line that to our report section here. We can see in this example here we need to set up a template REF that wreck to the element before we can use this. So let's find out enough menu element, which is here, this div with a class of navbar dash menu. Let's add a ref to this. We could call this navbar menu ref or something like that. Let's jump down to the script section. A lot of comment here which says click outside to close. We need to set up our template href. We can see in this example here. We'll create a constant with the same name as our ref, which is where has it gone? Enough, bomb menu ref. So const navbar, menu ref dt equals ref with a default value of null. Now we can use this composable. So let's copy this line here. Paste that here, change the target to this href here. So I'll copy that, paste that there, save that. And let's show the mobile menu. Clear the console and click outside somewhere. And we could see the events objects being locked out here. So that seems to be working. But we don't want to just log out the event object. So I'm gonna select all of that and add curly braces and add a new line in the middle. This Show Mobile Nav ref determines whether or not the mobile menu is shown. So we can just set this back to false. Once we've clicked outside. In this onclick outside method, we can just do Show Mobile Nav value equals false. I'll just remove the event object from here because we're not gonna need that. Save that, and let's see if it's working. Show the menu. Click outside. We see it disappears. However, it's now not closing the mobile menu when we click on the X. Let's see if we can fix that. Let's jump up to that button, which is here. I think I see what's happening here. Mobile menu is currently showing what did we click on the burger button. Because this is outside of mobile menu. It's triggering the onclick outside event on setting Show Mobile Nav back to false on hiding the mobile menu. What we calls. We also have this click handler on the burger button. This is then firing this code, which is going to set show mobile nav to the opposite of itself. This point is going to set it back to true. If we jump back to the onclick outside documentation and scroll down a bit, we can actually add an options object as a third parameter after the target, the handler, where we can have this ignore option to tell the composable which elements to ignore the onclick outside functionality. We could just make it ignore all clicks on this navbar burger. Now we need to set up a template ref first for this bob agar. Let's add a ref to this settler T2 nav bar, burger ref. Save that down. We need to set up data ref for that here, I'll duplicate this one. Change this to cost navbar burger ref. Then in this onclick outside function, after our handler function, we can add comma and then an object that inside that we cannot ignore option. Set that equal to an array, pass in the template ref solve any elements we want this composable to ignore. So let's pass in this navbar beggar F, paste that in there, save that. Let's see if that's fixed. Click on the button. We could still close it by clicking on the X. Let's see if we can still click outside to close it. And yes, we can. 100. Delete Modal Design (Reactive Objects): Okay, so our app is basically complete. In this out we've covered almost everything that we covered in the earlier modules. There's just one more thing that I want to add, which is a modal that gets displayed when we click on Delete button to prompt the user for confirmation before the note is deleted. The reasons I want to add this are number one, it will allow us to add a reactive objects to our app, which we haven't used yet. Number two, it will allow us to use some lifecycle hooks which we haven't used yet. Number three, it will help us to solidify our knowledge of data on events across parent and child components using props, emits, model value and update modal value. And I think it's a good idea to go over this parent-child stuff again, because these concepts can be quite confusing, yet they're really K for creating solid composition API apps. First, let's create a new component for our modal. I'm going to jump to source components, notes. And I don't want to create a new file in this folder called modal Delete Note dot view. For now, just gonna stick out template tags in there. Dave. I'll just put the texts. This is a modal. Save that now on our notes component note dot view. Let's jump to that. Components notes. Nope, dot view. Let's add a reactive objects where the property for determining whether or not this modal is shown. I'm going to import ref review, so I'll add that here. Then I'll scroll down to the bottom. I'm going to add a block comment which says modals. I'm going to set up a new reactive objects called modals. Modals equals reactive, reactive that we need to import from view, not href. Let's replace that with reactive. Passing the object. And I'm going to add a property called Delete Note Set dot equal to false. This property will determine whether or not the modal is shown. If it's false, we don't show it. And if it's true, then we do show it. The reason I've used a reactive object here is because in a lot of apps like this, we might have a bunch of different models. For example, right now when we edit a note, we jumped to a new page, but we might decide to replace this functionality with a modal for editing the notes. This reactive object gives us a place where we can manage all of our models together. You might have another property here, edit notes, and then use this to manage edit notes modal. But for now we're just going to add this delete notes modal, so I'll get rid of that. Save that. Let's import our new modal component and only display it when this delete notes property is true. So we'll jump to our inputs. We're going to import modal delete note from slush components slash notes, slash modal delete, notes dot view. I'll save that. And now let's place this after our footer element modal. Delete note. Save that. And we can now see that on both of our notes. But let's only display this when modals Dot Delete Note is true. So a lot of v-if directed to this modal v dash, if modals, dots, delete, note, save that. And we see the modal disappear. If we change modals dot delete node to true, then we see it reappear. Let's set that back to false and save that. Now let's use bullets to create a nice-looking modal. So let's jump back to the ball mill site on the.io docs to components and modal. Let's scroll down a bit here. We're going to use this modal card example, which looks pretty nice. I'm going to copy all of the code for this. Jump to modal, Delete, Note dot Vue. Just paste all of that in here. Get rid of the div that we added before. In dense it all a bit and save that reload so that we can actually see this modal when we click on Delete. Let's jump to our Delete button on Nope dot view, which is here. Instead of triggering our delete notes, action in our store, will instead just show this modal. To do this, we can just do modals, Dot, Delete, notes equals true. Save that. Click on Delete. We're still not seeing the modal. Let's see if it's being added to our mock-up here. So I'll inspect this note. We can say the modal has been added to the page, but we're not seeing it. I think that's because we need to add a class of active to the modal. Now we can see it. So let's jump back to our modal component and add this class to the root is active. Save that. Now let's click on the Delete button. We can now see the modal. Now let's modify this a bit. Let's change this title here, modal title to delete notes, question mark, save that. And let's add some content to the body hair. So inside this modal card body class, we will just add, you. Sure. You want to delete this note, save that. And we can now see that message. Now I want to align these buttons over to the right. If we drove to this footer element with a class of modal flashcard dash four. We can do this by adding one of Ballmer's Flexbox classes, which is just to phi dash, dash, dash and say, actually I think it's justify content flex and let's try that. Save that. Again, you can find all of these classes documented on the former site. Okay, now I'm going to swap these buttons around. Put the cancel button first inside this footer. I don't want to change the text in this Save Changes button to delete. I think it makes sense to make this button red so we can just change. This is success. Class two is danger. To make it read. This modal is looking pretty good now. Now we just need to get it behaving correctly. So that number one, we can hide it when we click on this X or the Cancel button. If we click outside of the modal. To do that, we're going to use our click outside composable from view use again. We also need to get this to actually delete the note when we click on the Delete button. We're also going to add some keyboard control to this so that the user can hit Escape to close the modal. To do that, we're going to make use of some lifecycle hooks. 101. Hide the Delete Modal (modelValue & update:modelValue): If the user clicks on this council button or this exploits in them, we want to hide this modal. Now these buttons are in our child component. Modal Delete Note dot view. But if the user clicks these buttons, then we want to modify the modals Dot, Delete Note property which is on the parent component. One way we could do this is by emitting a customer event when we click on these buttons. And then in the parent component, note dot view, listening out for that event. And when it's received. Set modals Dot, Delete, Note, box or false. However, as we learned earlier in the course, we don't actually need to do this. We can't get a child component directly modify a data property which is on its parent component. By passing this reactive data property down to the child component using V-model, then receiving the value of V model using the special model value prop, and then updating the source of that modal value on the parent component by using update model value. Well, first of all, let's pass down modals Dot Delete Note to the child component using V-model. Let's jump up to modal delete no component here. I'm allowed av, model directive and set.seed the same as the modals Dot Delete notes, paste that in there, save that. And we can now receive this V model using the special modal value prop, the child components. So let's jump to modal delete nope dot view. The script section on the attribute sets up our props. So a lot of comment here which says props. I'm a let a constant called props and set that equal to the define props method pass in an object and we'll define our props here. To receive this data property that we're passing down with V model, we need to use the model value prop, set that equal to an object. The type is going to be billion because modals, Dr. Lee No, is a billion solely going to be true or false and almost set the default to false. To make sure this is being passed down correctly and received correctly. Let's just output this modal value prop in our template. So after the texts that we added to the body, I'm going to add a pre tag and just output double curly braces and modal value. Save that, reload the app, click on Delete. And yeah, we can see that's getting through because we can see true here. Now let's just remove this pre tag. Save that. Now let's create a method that can be fired when we click on the X button or the Cancel button. So let's jump down to the script section in modal Delete Note dot Vue. Going to add a section called close modal. I'll create a constant called close modal settler equal to an arrow function. For now we'll just log out close modal. Save that. Now let's trigger this method on our buttons. So the cancel button, just going to break this up onto multiple lines. Split the attributes. Melodic click handler, click equals close modal. Then I'm going to copy this click handler, find this little exports it. Where is that thing? Here is, I'll split that onto multiple lines. Split the attributes, just paste in that click on, Save that. Unless make sure this close modal method is firing. Click on the Cancel button, we see the log. Click on the X button, we see the log. Now we just need to emit the hope DAG model value event in this method. First of all, we need to define our myths. Let sod our mitts section, create a constant called emit. Set that equal to the defined emits method pass in an array. We'll just add the updates colon model volume. Say that we can now emit this special event in our clothes modal method. We can just do emit and then hope day modal value. So this is going to change the source data property in the parallel component that we're passing down with V-model and receiving with modal value. It's going to update this Delete Note property in the pair of component note dot view, we need to tell it what values to set it to. And we want to hide the modal, so we want to set this back to false. Let's jump down to this emit. We can just start false as a second parameter. Let's say thought. See if it's working. Show the delete modal, click on the X, and that's working. Click on council. And that's working as well. 102. Delete Modal - Click Outside to Close: It would be better if this modal closed itself if we clicked anywhere outside of the car. That's the functionality we would expect from a modal like this. Remember we use that click outside composable earlier from view use to make this Mobile Nav Menu disappear when we click outside of it. We could use that again. Well, before we do that, I don't like the fact that there's no padding around this modal. When we're on a smaller screen ticket, it look better with a little bit plotting this div with the class of modal and just kind of add a class of H2 to give that a little bit of padding. That looks a bit better. I think. Let's use that click outside composable here so that we can close this modal. Let me click outside of the card. I'm going to jump to the Explorer. I'm just going to right-click the source folder and choose Find in folder and search for click outside. There is an unopposed. So let's jump to that. I'm going to copy this input. Then jump to modal delete node dot view. On this too, It's up. Import, Paste that jump. But to not Bardot view. Now let's copy all of this code. We might as well copy the comment as well. Jump back to delete modal. I will paste this at the bottom. Remember we need a template ref to tell this composable which element we're going to be clicking outside of two, then do something. Let's replace this navbar menu ref here and here with what we are going to be clicking outside of this modal dashed card div. We could call this modal cards ref. Let's add this to the modal card. The attributes. This href, modal cod ref, save that. I don't think we're going to need this ignore option. I'm just going to remove this third parameter, this objects on this comma as well like that. And we can get rid of this data ref, which we were adding to that ignore option. Now when we do click outside, we just want to hide the modal by triggering this method close modal. We can actually just select all of this handler here from the closing curly brace to these parentheses. I just put the name of our method close modal. Let's save that, see if that's working. Reload, show the modal. We have an error here. Href is not defined. I guess we need to import the ref method from view. Imports from view, save that. Reload. Show the modal click outside. Yeah, it's working. Let's just make sure it's not being triggered erroneously, like it was on the navbar before. They only gets fired if we click right outside of the card. And the council and experts are still working. 103. Delete Modal - Keyboard Control (Lifecycle Hooks): We usually expect to be able to close a modal like this by hitting the Escape key on our keyboard. Let's set up some keyboard hadn't link in the mounted hook of this modal Delete Note dot Vue component. I'm going to jump to modal Delete Note talk to you, and down to the bottom. I'll create a comment called keyboard control. I'm allowed an unmounted hook passe and a handler to that like that. And we'll just log out mounted for now, say that we have an error here on mounted is not defined. That's because we need to import this lifecycle hook from view, slats out that hit on melted, save that reload, show the modal and we can say unmounted being logged out. If we hide it and show it again, we can say unmounted is logged out again. Now inside a multi-tool, let's set up an event listener for the key event, which will be fired after we press a key and then let go of it. Let's get rid of this log. And we can just do document dots, add event, listener, parentheses, and then follow the first parameter. This is the abutment going to listen out for, and we're going to listen out for the event. And the second parameter is our handler. So let's add an arrow function here. Let's also pass in the event object. I'll replace these parentheses with an a, and then we'll just lock that out for now. Console.log. Save that and reload, show modal and hit the escape key. And we can see the keyboard event being locked out. Within this event objects. We can see that when we hit the Escape key, this key property is set to escape. This key property is generally the property that we want to check in our JavaScript to determine which key was pressed. Let's check if the escape key was pressed. And if so, then we can just fire close modal method, thus hiding the modal. So let's remove this console.log. And we can just do if a dot k is equal to escape them, you could just fire the close modal. A method. Save that. Reload, show the modal, hit the escape key. And that's working. We see the modal disappear. However, we do have an issue here. If we look something out inside this event listener, we can just log out, close it. Save that. Now if I reload, click on Delete to show the modal and hit Escape. We see close it being locked out once. But if I show another modal hit Escape, It's now been locked out twice this time. And if we show a modal again, hit escape. This time it's been locked out three times. This is because even after the modal is closed, this event listener still exists. This event listener is not being removed when we close the modal. When we first show a modal, this event listener is set up. Let me close the modal. This is still sets up. Then we open another modal. We then sets up a second event listener. This part, we now have two of these event listeners firing. Got it. We'll keep adding a new event listener every tau we show a new modal. So what we need to do is remove this event listener when the modal is closed. And we could do this in the unmounted hook, which will be phi had went on modal is removed from the dom. Now first, to make an event listener removable, we need to put its callback function into its own named function. Both multi talk. I'm going to create a constant called handle. Keyboard. Set that equal to an arrow function. Pass in the parameter, the event object. Now I'm going to cut these two lines and paste them into this new function. Now we can just select all of this handler from the closing curly brace to the a. Just pass in the name of this function, handle, keyboard. Let's save that. I'll make sure that's still working. Hit the Escape key. That's still working. Now we just need to remove this event listener in the unmounted talk. After the unmounted talk with Loudon on unmounted hook, pass in a arrow function handler. Now to remove this event listener, we can just use the remove event listener method. I'm just gonna copy all of this line, paste line here and change out event listener to remove event listener. Save that. And we do need to add the unknown mounted hope to our imports. Let's add that here on mounted. Save that. Now let's see if this is working. Click the Delete button, show the modal hit Escape. We see our log being fired only once. Click Delete again, show the modal hit Escape. We now see close it only being logged out once again. So this is not working. The event listener is being removed when the modal is closed. And if we keep hitting Escape here, we don't see anything locked out. We can now just remove the console log from the handle keyboard function. Save that. Now all we need to do is delete this soccer. When the user clicks ON delete in the modal. 104. Delete Modal - Delete The Note: The final thing we need to do is actually delete the notes that the user is trying to delete when they click on this red delete button. Now we already have a Delete Note action in our store. Shelves, stores, and store Node.js to the options. We already have this Delete Note actions sets up which we can use to delete the notes. On all this oxygen is expecting is the ID of the node that we want to delete. So we can just trigger this oxygen directly in our modal Delete Note dot Vue component. Let's jump to that. On the delete button, which is here. We just want to trigger that action in our store. Delete note that's expecting an ID, but we don't actually have access to the notes ID within this component. However, if we jump to its parent component note dot view, then we do have access to all of the notes information here in this note prop. We could use this to pass the note's ID down to the child component modal delete node dot view using a prop. Let's jump up to a modal delete nope component, which is here. We could either pass down the entire note objects. We can call this note set that equal to notes. This would pass down the entire objects with both the ID and the content. However, we only actually need the ID. So we might as well only pass the ID down. To do that, we could pass down notes and maybe just change the name of the prop to note ID, camelCase. Save that. Now we need to receive this note's ID prop, the modal Delete Note dot Vue component. So let's jump to that. Jump to our props. I'm allowed this prop type is going to be string. I will make this required as well. I'll set required to true. Let's save that and make sure this node ID properties getting through. After our content here. The modal, let's just output pre-talk with double curly braces and note ID, save that, reload and show the modal. And yeah, we can see ID1 being spits out for the first note, an ID to being spit out for the second note. Let's get rid of this pre tag. We now just need to import our store, opinions store into this modal component, adjust, trigger this delete Note auction and pass in the ID. First we need to import it. We can just do import. Use store notes from slash stores slash store. Actually no, let's add slash stores slash store notes. Save that. We need to assign this store to a constant. So after our props and limits inside a store command, and just do const store notes equals use store notes, parentheses. Save that. And we should now have access to a store here. This Delete Note action within the store. Let's jump up to the Delete button, which is here. I'll split this up onto multiple lines, split the attributes onto multiple lines, add a click handler. We just want to fire the Delete Note auction that's in our store. So we can just do stole notes, Dot Delete, note, pass in the ID. And again, we're passing down the ID, this note's ID prop. We can just do stole notes Dot Delete notes, notes, ID. Let's save that and see if it's working. Reload, click on Delete. Click on Delete again. We can see the note was deleted. We can also see that the modal is being hidden automatically as well. Let me click Delete. And that's because the modal is a child component of note dot view. Note dot view is the component we're using to display a note. When we delete a note, that note dot Vue component gets removed from the dump along with all of its child components, including modal Delete, Note dot Vue component. 105. Course Roundup: Congratulations, You made it to the end. I hope you now feel ready to start building your own ups with V3 and the incredible composition API. I also hope this course has helped you to understand the massive benefits that come with the composition API over the options API. Make sure you retain the knowledge from this course by building apps with the composition API. As soon as you count, you could try rebuilding the Nope balls out from scratch on your own. Or you could try building a clone of one of your favorites ups. If you want to learn more from me and jump on over to Danny's dot link slash YouTube, where you can find my YouTube channel makeups with Danny, where I share tons of free content on Vue.js, quasar framework, beautify and VS code on much more. Also check out my other freemium courses are done iz dot link slash courses. All of the links to my premium courses on this page will automatically apply my special discount. So each course, I have a course on beautify, which is the most popular component framework for Vue.js. Unfortunately, at the time I'm recording this beautified doesn't support a few three, but hopefully it will soon. Then I have three courses on quasar framework, which is an absolutely incredible framework which allows you to create a Vue.js up with a single code base, deploy it to many different platforms. You can deploy it to a real app that can be deployed to the app stores for iOS, android, Mac, and Windows. You can deploy it to a single-page application and progressive web app server side rendered up even to a browser extension for Chrome or Firefox. The latest version of quasar, those support V3 and the composition API. However, at the time I'm recording this, these courses are for U2 and the options API. However, with all of the skills that you've learned in this course, you should be able to follow along with these courses using v3. I'm the composition API. Finally, I have a short course where I teach you the basics of HTML, CSS, and JavaScript. I'll attach links to my YouTube channel on Courses page to this lecture. Thanks for watching, and I hope you enjoy building stuff with the composition API.