Does your legacy app run on one of these?

Adding Vue.js to an Existing Project

Blunt Jackson
7 min readNov 13, 2018

--

Let’s say you have an existing web application that is not taking advantage of any existing reactive frameworks or technology, and you want to start incorporate some of that sweet, sweet functionality without rewriting your whole application from scratch.

Vue is a very elegant framework. To truly experience the beauty of it, you would incorporate the data model portion known as Vuex. We will look at that in a future piece. Once you start using Vue and Vuex, you may want to rewrite your whole application from scratch! But in the meantime, let’s jump into the thick of things in a platform agnostic way. I’m going to assume a completely vanilla webserver.

Let’s begin at the beginning.

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>

Drop those into your HTML headers, and we are off to the races.

Fundamentals

Everything begins with the Vue object:

const vueApp = new Vue({
el: '#vapp',
data: {
display: 'redbox'
}
})

When creating our application, we instantiate a new Vue object, and, in this case we provide it with two keys: el tells vue what dom element to live on, and the data key provides some data available to our application.

For this first exercise we are going to create an app that provides an introduction that includes: display, data, and re-usable components.

Let’s integrate all of this into a single file so you can play along at home, but recognize that this could be one tiny piece of a much larger and pre-existing web page:

<DOCTYPE html>
<html>
<head>
<title>Vue Demo #1: The Box App</title>
<meta charset='utf-8' />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h1>Vue Demo #1</h1>
<div id="vapp">
<p>Hello World.</p>
</div>
<!-- Our View App goes at the end of the document -->
<script>
const vueApp = new Vue({
el: '#vapp',
data: {
display: 'redbox'
}
})
</script>
</body>
</html>

Got it? Great. Obviously, this doesn’t actually do anything yet. Want it to? Let’s just access some data real quick: Let’s change our hello world statement:

<p>Hello World: {{ display }}</p>

Easy peasy. Vue is doing something.

Components!

But let’s dive a little deeper and add a component, which is the true heart of Vue. Before our Vue app is instantiated, let’s define and register a component:

Vue.component('ColoredBox', {
template: "<div class=\"box\"> - </div>"
})

Additionally, let’s drop the component into our markup, replacing that paragraph:

<div id="vapp">
<colored-box class="red"/>
</div>

And finally, let’s drop a little internal style sheet up into our headers:

<style type="text/css">
.box {
height: 200px;
width: 200px;
text-align: center;
}
.red {
background-color: red;
}
.green {
background-color: green;
}
</style>

Are you getting a bright red box? Fantastic!

Are you distressed by the fact that the component was declared as ColoredBox while the element is known as <colored-box />? Fantastic!

Vue does a little magic to map “kebab-cased” html tags and attributes to camel case javascript. One technical reason for this is that html tags are not case sensitive, so even though Vue is helping us expand beyond html tags, most developers will take advantage of this magic. (Personal confession, I don’t care for magical translations, so will often use the kebab-case tags everywhere, which works just fine, but interferes with autocomplete.)

Finally, are you confused as to why we have one class defined in the component template, and another on the component tag? Well, one reason is to demonstrate that this is possible. Another reason, however, is to lead to the next concept…

Conditional Display

Vue offers a directive v-show which can be used to determine whether a component should be displayed or not. This is one important way of creating single-page dynamic applications. Here’s how we are going to enhance our code:

First: add a second component, and add the v-show attribute:

<colored-box class="red" v-show="display == 'redbox'" />
<colored-box class="green" v-show="display == 'greenbox'" />

You probably noticed the css styling for our green box previously, so this should come as no surprise. And, if everything is working well, you should only see the red box. How is v-show doing this?

Scroll up and review our Vue app. It included a section called data:

const vueApp = new Vue({
el: '#vapp',
data: {
display: 'redbox'
}
})

Our v-show directive is testing to see whether the display key contains redbox or greenbox. So, let’s add the functionality to toggle these:

First, let’s update our component to add a button:

template: "<div class=\"box\"><button v-on:click=\"$emit('toggle')\">Toggle Now</button></div>"

Notice that with the Vue directive v-on:click we set the behavior to $emit('toggle'). This sends an event to the parent. The parent could be a higher level component or, in this case, the root level application.

To wire this into the root we need to add another v-on directive, this time on the control tags:

<colored-box class="red" v-show="display == 'redbox'" v-on:toggle="toggleBox()"></colored-box>
<colored-box class="green" v-show="display == 'greenbox'" v-on:toggle="toggleBox()"></colored-box>

In addition to our display logic, we now listen for that toggle event, and when it is received, we call the togglebox() method. Where’s that? We will now add it to our Vue application in a new section, methods.

const vueApp = new Vue({
el: '#vapp',
data: {
display: 'redbox'
},
methods: {
toggleBox() {
this.display == 'redbox' ? this.display = 'greenbox' : this.display = 'redbox'
}
}
})

Put this all together and you should have a button labeled Toggle Now that switches back and forth displaying the green box and the red.

It may seem like this event handling is a bit awkward. It could potentially get even more awkward if we have components nested several deep, and each parent component needs to receive the event and possibly rebroadcast it up the chain.

It turns out there are a variety of ways to skin this cat, including using state in a Vuex store, to using Vue to create an EventBus. But in a design pattern where certain major display decisions are always going to rest in the application itself, there is a very convenient short-hand option that cuts right to the chase. Here’s the full app simplified to using the $root reference, included in it’s entirety to check your work.

See if you can notice the simplifications. What might be the disadvantages of this approach?

<DOCTYPE html>
<html>
<head>
<title>Vue Demo #1: The Box App</title>
<meta charset='utf-8' />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style type="text/css">
.box {
height: 200px;
width: 200px;
text-align: center;
}
.red {
background-color: red;
}
.green {
background-color: green;
}
</style>
</head>
<body>
<h1>Vue Demo #1</h1>
<div id="vapp">
<colored-box class="red" v-show="display == 'redbox'"></colored-box>
<colored-box class="green" v-show="display == 'greenbox'"></colored-box>
</div>
<!-- Our View App goes at the end of the document -->
<script>
Vue.component('ColoredBox', {
template: "<div class=\"box\"><button v-on:click=\"toggleMe()\">Toggle Now</button></div>",
methods: {
toggleMe() {
this.$root.toggleBox()
}
}
})
const vueApp = new Vue({
el: '#vapp',
data: {
display: 'redbox'
},
methods: {
toggleBox() {
this.display == 'redbox' ? this.display = 'greenbox' : this.display = 'redbox'
}
}
})
</script>
</body>
</html>

At this point, you should be able to see how Vue can make a single-page application easy and functional.

But let’s play with data, too, just to give you a taste.

Data Display and Modification

Let’s add a clicker to our component that counts clicks. We will need to make three enhancements:

  1. A clicker to click.
  2. A way to count our clicks.
  3. A clickety click display.

We can do all of this in our component:

Vue.component('ColoredBox', {
template: "<div class=\"box\"><button v-on:click=\"toggleMe()\">Toggle Now</button><br><button v-on:click=\"clicks++\">Clickety Click</button><br>{{ clicks }}</div>",
data: function() {
return {
clicks: 0
}
},
methods: {
toggleMe() {
this.$root.toggleBox()
}
}
})

Well damn. That template is getting UGLY.

This is why many people like to use server side assembly to write a beautiful looking template that gets packed into this form. You could also use backtick notation for multi-line strings, but be aware that this is not universally supported:

template: 
`<div class=\"box\">
<button v-on:click=\"toggleMe()\">Toggle Now</button>
<br>
<button v-on:click=\"clicks++\">Clickety Click</button>
<br>
{{ clicks }}
</div>`,

Notice how our new button just operates on that clicks member directly without going through the hullabaloo of a method call.

Also, I hope you noticed that the data section in our component looks different from the section in our application. It is returned from a function, rather than referenced as a simple hash. This is so that each and every instance of the component has it’s own click member. If we included the data block just like we did in the application we would (A) get a warning in the console, and (B) every component would share the member, which is probably not what we wanted. And if we did want that, putting it in our Vuex store, or referencing it at the app level would make more sense.

Conclusions

This is a tiny taste of what Vue offers, but hopefully it is enough of a taste to inspire you to start incorporating Vue into your existing application, no matter how “legacy” it is.

That is a punch card reader. That gentleman is either loading a program to run, or loading data for a running program, or both. This was a big time-saver for developers back when woolly mammoths roamed the steppes.

Other articles in the Vue series:

--

--

Blunt Jackson

Building web applications since 1992. Crikey, that’s a long time.