How to Make a Popover with Vue
Making a popover with Vue is pretty straight froward. It's a great component to make if you're new to Vue and want to learn the basics of making components. They're also a great use case for slots. Slots are a useful feature of Vue that allow you to pass html into components. This post will show you how to make a basic popover component that could be built upon for your use case. If you're already familiar with Vue and just want to see the code you can here.
Even though this is a simple component it demonstrates some important features of Vue: dynamic styles, emitting events, and slots.
Sections
Single file components
The most common way to make Vue components is as a single-file component (SFC). This is one of its big differences with Angular and React. It's one of the things I like about working with Vue. A SFC usually consists of three sections: template, script, and style. Below I'll go through each section for our Popover SFC.
The template section
Our markup in the <template>
section is pretty simple. We don't really need anything too complex.
There are three attributes attached to the <div>
. A normal css class
tag and two common vue directives: v-if
and :style
. The v-if
will be used to toggle showing the popover and the :style
for binding dynamic styles to our component. In our case will use it to position the component.
Slots
We're also using the special <slot></slot>
tag. Slots are a way to distribute content to components.
So when we actually use the component like this:
whatever markup we put in between the <Popover>
tags will be rendered. In this case the <h1>
will be rendered in the slots place. If I didn't need the flexibility of rendering different tags I would probably just use a string prop with the text I want to display. Custom components can also be used with slots. That is how I often use them.
There's a bunch of stuff you can do with slots like have named slots so you can have more than one slot in a component. and have default content for the slot. Here's the docs for slots for a deeper dive.
The script section
The script section is responsible for much of the functionality. We're keeping it pretty simple but it's a good base to expand upon if you'd like.
The props
property defines the data props we'll pass into the popover. All of the props options: type, required, and default are optional. I always include them though since it indicates what the components expect and will cause the compiler to throw a warning if they're invalid.
The show
prop will act as a flag to show and hide the component. It's used in the v-if
to display the component. We set default: false
to hide it by default.
We'll use xCoordinate
and yCoordinate
for the values to position the popover on the page. In the styles section we'll see that we set the components position to absolute which will allow us to position the component we're we want on the page.
The computed
property is used to filter, transform or consume data. Usually from data stores, AJAX calls or props. The more components you make you'll find that it is often used. The data in the computed properties is get only (read only) by default and in most cases you don't need to updated them. They will update when the data the are derived from changes. If you do need to update them you will need to define a setter. This is used in more complex use cases though.
We only have one computed property, position
which returns an object with a key of left
and top
. We will then use these as a dynamic styles to position the popover. It's used in the template with the bound style :style=position
in the template section. The other styles we will just use as normal in the style section.
The style section
I really like the way styling works with Vue. Using the scoped
attribute confines the properties to this component. After making a handful of apps with Vue I've found this to work great in practice. It makes updating styles pretty easy in that you don't have to worry about changing a global style that effects other parts of the application. I usually just end up with a handful of global styles.
Here's our style section:
This ia all pretty standard styling. The most important one is position: absolute
which in conjunction with the dynamic style positions the popover.
Emitting a custom event
Our component is almost complete but we don't have a way to close the popover. Let's add a custom event @click="$emit('close')"
to our template.
Now when we click the component it will emit a close event. In the parent component we need to define what to do when a close event is fired. So we'll add the action like this: @close="showPopover = false"
.
Why emit the event at all?
Vue has one way data binding. Emitting an event allows us to communicate with the parent component. Parent communicates to children through props. Children communicate to parents through events. This ensures that your components update smoothly and predictably.
The complete component
Using the popover
Let's take a look at how we'd actually use the popover in a simple example. This should be treated as a snippet it might have some code omitted. Take a look at this repo for a full example.
To position the popover we're capturing a mouse click event and in displayPopover(event)
setting the xCoordinate
and yCoordinate
with event.pageX
and event.pageY
.
Going further
I kept this component pretty lean but there are a lot more things that could be added to it. There are many ways we could have positioned it. We could have attached the Popover to an element using the element's id instead of using coordinates or allow for both. Another option would be to define a position component prop that could take the values: top, bottom, left, or right to orientate the popover.
Slots are also a great feature to make use of in your Vuejs components and worth some more time exploring.