Creating a Vue.js Component

finding the right slot

Colin Fallon
6 min readFeb 17, 2017

I’ve been taking a look at vue.js recently. It has many similarities to react, but I find it more accessible — i.e. easier to learn. And the separation of html into templates gives it that familiar and comfortable feeling that I often yearn for.

Vue.js also has an optional plugin called Vuex. This introduces state management along the lines of Flux. In fact, Vuex follows the Flux architecture pretty religiously and, since you may remember that I am a big fan of Flux from way back, you will not be surprised that I strongly recommend Vuex for anything more than the most trivial of projects. I will certainly be using Vuex anytime that I use Vue.js.

Proving the rule

Of course this means I will not be using Vuex in this article! Well, the focus here is on how to create a UI component in Vue.js v2 and I don’t want to take the focus away from that through any confusion around what I’m doing with state management. Although, having now spent quite a few words talking about state management you may feel that I have already failed on that one!!

Time for dialogue

Ok, so it’s finally time to start talking about building a vue.js component. I’ve chosen to create a modal dialog component for this example and have imaginatively called it modal-vue.

The functionality is based on Kris Jenkin’s Elm library, elm-dialog which I used on a recent project. Basically it needs to be accept a customisable header, body and footer for the dialog display together with a few props to control things — such as whether the dialog should be displayed or not.

Anatomy of a component

The structure of a vue.js component is simple and intuitive, with three distinct parts for html, javascript and css respectively. The complex diagram below sums it all up.

JavaScript for gurus

I like to think of myself as a javascript programmer so let’s start with the javascript within the <script> tag of our component.

It goes like this

export default {
props: {
showModal: Boolean,
closeAction: Function,
containerClass: String
}
}

Basically it defines three props that the component can be passed.

  • showModal, a Boolean that controls whether the modal dialog should be displayed or not
  • closeAction, a function to be called when the dialog close button is clicked
  • containerClass, an optional Bootstrap container class for the <div> that wraps the dialog

This is not one of those articles where the author starts with something basic, and then builds and builds on it until your head hurts. That’s it — there will be no more in the <script> section!

HTML template

You can use a javascript render function approach in vue.js just like react, but the most common and, for me, most intuitive approach is to use a template. For this particular component everything happens within the <template> tag.

So the key thing that you will no doubt have already spotted is that vue.js has it’s own DSL that is embedded within the html tags. And this is something you will need to learn in order to use vue.js. But it’s very straightforward and if you’ve ever used any other template attribute language of which there are many (dare I mention Zope!) then you will very easily pick it up.

Vue,js directives tend to start with v- such as v-if in the gist above, and you can find full details of all the directives available in the Vue.js documentation, which is an excellent resource.

binding

Let’s start at the top with

<div :class="containerClass">

Ok, I am showing off already here! :class is actually short for v-bind:class but I would drop the v-bind right from the get-go. Basically if you start an attribute with a colon : it means that this is a dynamic attribute and the value of that attribute will be evaluated as code.

So <div :class="containerClass"> does not mean that this div will have a CSS class of "containerClass". containerClass will be evaluated as code and as we have already defined it as a prop within our <script> section this means that the value of the containerClass prop that we pass to the component will be used for the CSS class.

binding++

The binding gets a little more sophisticated in the next line:

<div :class="{modal: true, in: showModal}" :style="{ display: showModal ? 'block' : 'none' }">

In vue.js when you bind an object to the class attribute, then for each property where the property’s value equates to true that property will be part of the html class attribute. So for {modal: true, in: showModal}, modal is always true and in will depend on the value of the prop showModal. So if showModal is true this will resolve to and attribute of class="modal in".

For the style attribute, this is just straightforward Javascript and, if showModal is true again, this will resolve to style="{display: block}"

slotting it in

Passing in the potentially lengthy formatted html for the header, body and footer of the dialog is not something that fits well with passing in props as attributes within an html tag.

Happily vue.js has a very good way to do this — slots!

Slots are apparently modelled on the W3C web components draft spec. When I first read this, I have to admit that my heart sank. My impression of the W3C web components spec was that it is one of those things that have been in draft forever — and I imagined an endless talking shop while the rest of the world moved on! But when I actually looked (radical, I know) at what they are proposing it seems pretty good!!

Anyway, the crux of it all is that the simple markup of

<slot name="header"></slot>

defines a slot for the header content. We’ll look at how to pass the content into the slot in due course, but basically the <slot> tag will be replaced with that content.

slots with attitude

To implement a dialog with Bootstrap, I need to wrap the header, body and footer in a div with the corresponding Bootstrap class. So, for example, the footer content should be wrapped with <div class="modal-footer">.

But wait a minute, the footer is optional and if no footer is passed I do not want my component to render an empty <div class="modal-footer"> - I just want there to be no footer.

Fortunately, vue.js v2 provides some instance properties that allow you to inspect your component. One of these is $slots and it allows you to access the value of each named slot. So this.$slot.footer will return the value of the footer slot. Bingo!

So in our template we can use the v-if directive to only render the entire div <div class="modal-footer"> if a footer slot has been passed to the component. This is done by the markup:

<div v-if="this.$slots.footer" class="modal-footer">

Using our component

And finally, we reach the point where we can start to use our great new modal-vue component.

The gist below shows a fairly simplistic example.

The data property showModal is used to control whether the dialog is displayed and it is passed as a property in the <modal> tag in line 6, together with the closeAction prop which defines the method to be called when the dialog close button is clicked. In this case that's the closeDialog method and all this does is to set showModal to false (line 26), thereby removing the dialog.

This example passes a header and a body slot into the dialog for display and these are just html with the appropriate slot attribute.

And that’s about it!

Further reading

You can see the modal-vue component in action in this example.

You can find the full repository for the modal-vue component on GitHub. This includes the code for the examples and a way to run the examples locally if you want to experiment.

And perhaps create and share your own vue.js component!

--

--