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.