Step-by-Step: A Simple Vue.js App

Photo by Lindsay Henwood on Unsplash

Let’s build an app! It’s going to be simple and I’ll do a lot of explaining — a kind of Vue.js show-and-tell, for newbies. If all goes well, you’ll learn how Vue sees the world, feel inspired to learn more about Vue, and have the confidence to start implementing your ideas. We’ll build a single-user voting app, feature by tiny feature, beginning with an empty text editor.

Step 1: A Blank Page

Create a new text file and name it index.html (or whatever). Inside, add the basic boilerplate code for an HTML page:

Step 1

There’s nothing special here yet — no content, no JavaScript, just an HTML5 skeleton to build on.

Step 2: Proof That Vue.js Works

The next goal is to prove that Vue.js is running properly in the page. You’ll pull in the Vue library, create a Vue instance, and have the instance render a message. Create a new empty file called index.js (you can choose another name, but be sure to change the script tag at the bottom of index.html so that the src attribute points to the name you chose). Then, modify your HTML and JavaScript files to match the code shown here:

Step 2

So, what changed since the last version? For starters, you now have a JavaScript file, where you created a new Vue instance and passed it a configuration object. Let’s take a look at the contents of that object.

The el field tells Vue where to bind itself on the page — in this case, you created a new div with id app and bound Vue to it. Vue then treats this div as the boundaries where it will do its work — nothing outside of that div can affect (or be affected by) your code.

The data field stores the initial values for the state (or data) you want Vue to keep track of. As your app grows, it will read and write the contents of this data in various ways. At the moment your data contains just one value, “Vue is working!”, which you’ve named message.

Besides el and data, there are lots of other useful fields you could add. To learn all about them, I recommend you browse the Vue.js guide and API documentation …eventually. For this little app, though, we’ll only need a few and I’ll walk you through them as we go.

Turning to your HTML file, take a look at the curly brace “mustache” syntax inside the app div, where it says {{message}}. Those mustaches are one way to grab values from Vue’s data and show it on the page. Depending on your background, it could help to think of the app div as a template that guides what Vue does with its data.

Step 3: Data-Driven HTML Elements

Showing a string was a great first step. Let’s try something a little more complicated: creating new HTML elements on the fly, based on an array of values in data. In our case, the array will hold information about JavaScript frameworks. Not much information, mind you, just a name and a vote count. Here’s the code… take a look, modify your files to match, and then we’ll dissect it.

Step 3

Okay, so first check out the JavaScript and notice what you’ve replaced message with. Instead, you now have an array called frameworks, with three objects inside. Each object has a string name and a number votes.

Then in the HTML, you’ve added a ul element with a li element inside. This li has an unfamiliar v-for attribute on it, and some text contents (including some text in mustaches, {{}} and {{f.votes}}).

One way to read what’s going on in with the v-for is: “Let each item in the frameworks array create its own li element on the page, one by one. Inside each li, the particular item that created it will be known as f.” So, given your current data, this v-for causes three li elements to be generated. In the first one, f is {name: 'Vue.js', votes: 0}. In the second, f is {name: 'React', votes: 0} and so on. You use the mustache syntax here just like you did before with {{message}}, as a way to render name and votes within each li.

Step 4: Modifying Data

Right now, if you’ve followed along, you have a three-way tie of zero votes. Let’s add some voting buttons we can use to increase the votes count for each framework. After this step, the contents of your files should match this:

Step 4

Looking at the JavaScript first, you’ve added a new entry to the Vue instance’s configuration object: methods. This is an object whose keys are function names, and whose values are function implementations. You’ve also defined a new function inside it, called voteFor. This function expects a framework object, whose votes count should be increased. It increases votes by one, and then exits.

In the HTML, you added a button element, and set up a Vue event listener with the v-on attribute. You can read v-on:click="voteFor(f)" as “when this button gets clicked, send f to the voteFor method.”

Notice that after clicking the button, the li shows the new vote count immediately without any further hassle on your part. Since votes is inside the data object, Vue monitors it for changes and refreshes the relevant parts of the page when necessary.

Step 5: Creating Data

At present, you’re limited to voting on the three frameworks we hard-coded into our JavaScript. Let’s lift that restriction and let users add new ones to the list. As you modify your files to match mine, you’ll add a text input box for the new name, and have Vue listen for the Enter key. You’ll also define a method that the event listener can call, which will grab the input’s text from the event, build a new framework object, add it to the list, then clear the input. Take a look:

Step 5

This time let’s check out the HTML first. You’ve seen most of this stuff already, except the v-on:keyup.enter event listener. Note that we don’t seem to send any parameter to the addNew function, so how will it know what the user entered? Hold that thought, then flip over to your JavaScript file.

Over in the JavaScript, the addNew function does accept a parameter, even though we didn’t explicitly send one in the event listener. That’s because, by default, Vue event listeners send the event itself to event handlers. This is handy, since the event object has useful attributes on it.

Here, in particular, points to the text the user typed into the input. So, you can use as the framework name when you build the new object for pushing onto the frameworks list, and you can set to the empty string when you’re done, to start fresh for the next entry.

Note also the use of this.frameworks when pushing the new framework onto the list, instead of using or just frameworks. In the HTML template (mustaches, v-for attributes, etc) it’s frameworks, but in the JavaScript code it’s this.frameworks. This discrepancy might trip you up for a while, but eventually it ends up feeling nice and clean to leave it off in the HTML, and it ends up feeling clear and helpful to have things walled off behind namespaces in the JavaScript.

Step 6: Deleting Data

With the ability to add frameworks, you’ve also allowed users to make mistakes, such as typos ending up in the list! Let’s give users the ability to remove frameworks they’ve added. As you might expect by now, this will involve a new event listener and a new entry in methods.

Step 6

Since deletion is a very different user intent than voting, I decided to make it visually distinct by using an anchor tag instead of a button. Other than that, there should be no surprises in the HTML— you added a v-on:click event listener, which passes f to a new function remove. In the JavaScript, that remove function works by filtering frameworks to only include things that aren’t f.

Step 7: Persistence Using LocalStorage

Currently, each time you refresh the page, you reset the app’s state to the hard-coded framework list. To fix this, let’s implement simple save and load functions that use the LocalStorage API. Modify your JavaScript file to match what’s below, and then we’ll discuss what you did.

Step 7

First let’s look at save. LocalStorage can only store a string, so you encoded frameworks using JSON.stringify. Then you used localStorage.setItem to save it in the browser’s storage, so your app can retrieve it later. Glancing through the code, you can see that you added calls to after every modification to frameworks, so the saved copy is always up-to-date.

The load function is almost as simple as save, with a slight twist: When the user visits for the first time, or if storage gets cleared, there won’t be any saved data to load. Because of this, we do a quick check to make sure our loaded string at least has something in it before we parse it and set the value of frameworks.

It’s probably not immediately obvious where to call your load function. Fortunately Vue.js supplies a few special functions called lifecycle hooks, for situations like this where you want to do something at a particular moment. In this case, a reasonable place to load your data would be right after the Vue instance is created, which corresponds to the created lifecycle hook. There aren’t very many lifecycle hooks, and they’re useful to understand, so you may want to check out what the Vue.js guide has to say about them.

Step 8: Hiding Elements

You’ve added some nice features, and things are getting a little cluttered! The app’s most important features are viewing and voting, so let’s try hiding the delete links and input box until we need them, by tucking them behind an “edit mode” toggle. Copy my edits, and then we’ll talk about what changed:

Step 8

Let’s talk about the JavaScript first. In your data object, you added a boolean editMode that starts out false and will keep track of whether the app should show the add/delete UI elements. Then down below in methods you added a simple function to toggle the value of editMode. In designing this app, I decided not to bother with preserving editMode through a page refresh, but feel free to implement that as a personal exercise!

In the HTML you added a new conditional rendering attribute v-if="editMode" on everything that should only appear during Edit Mode. You also added a button, with an event listener that calls toggleEditMode. The text of the button changes to “Edit” when editMode is false, and “Done” when editMode is true. Instead of using the JavaScript ternary operator like I did, you may want to try implementing the same behavior yourself using v-if (and consider exploring v-else too).

Step 9: Computed Properties

One last feature to add — a section to call out the current winner(s). There are a few edge cases to consider, like when several frameworks have the same number of votes, or when there are no frameworks in the list.

Think for a second about how you might design this, or even try your hand at an implementation before continuing on.

Ready? OK, so you might choose to define a new array in data to store the winners, with a method to keep that array updated. You would need to call that method after every vote and during loading to keep it fresh.

That could work, but Vue.js has a more convenient way called computed properties. Take a look:

Step 9

In the HTML, you render the value of winnerString using mustaches, just like you did previously with frameworks, which was a variable contained inside data.

In the JavaScript though, we see that winnerString is not a variable inside data, but rather a function contained within computed. Inside computed is where you can put any derived values that are basically read-only, but which must be kept up-to-date with changes to the variables (such as vote counts) that they rely on. Vue happily lets us access winnerString like a variable in data, but caches its return value and automatically refreshes it whenever the votes change who’s winning, without us needing to manage all of that. Fewer headaches and less code to write. It’s pretty great.

The implementation of winnerString itself uses map, filter, and apply to grab the highest vote count, create a list of all frameworks having that many votes, and build a comma-separated string of their names.

Step 10: Your Turn

Have ideas for how to extend this? How about calling out the losers like we did with the winners? Sorting the list by votes? CSS to highlight the winners? Better layout & styling? Go forth and hack!

Thanks for Reading!

If you found this useful or interesting, please give the clap icon a few clicks below. Not only does that help other readers find this more easily, but also it tells me that I was able to help you out, which motivates me to write more guides like this one. Thanks again, and let me know what you build!