- Learn by example
Make an RPG
Vue Implementations

How to Create a Tree View in Vue

Why build your own Tree?

First of all, why would you even want to create your own tree view anyway? There's several of them out there already. You could just copy and paste the code or import it with npm and be on your way.

The problem is, you'll find that it's never exactly what you're looking for. Maybe it doesn't open and close the way you want it to. Or maybe it only takes raw text strings with no HTML tag customization. Or maybe you need right click functionality with a context menu that dissappears or reappears when the user does something in other parts of the page. Or maybe the input data format doesn't match your own. There's many reasons why you'd want your own custom tree.

Also, writing your own Tree gives you an understanding of it's implementation that far exceeds when you simply read through someone else's code. This is why we encourage you to modify the code in the code editor to the right, so you can learn how it all works. By writing the code yourself, it makes it easier to add new features. And when your boss asks you for estimates, you'll be in a much better position to make more accurate guesses, err estimates ;)

Data Tree

First, we'll start off with the data. We want each tree section to be collapsable. To collapse it using CSS we will need to wrap each section in a div of it's own. And to do that, we'll need each section to be an array of it's own. Hence, you need to structure your data into an array of Sections where each section contains a section_title and an array of anchor entries.

dataTree: [ { section_title: "Animals", entries: [ { link: "http://Crocodile.html", title: "Crocodile" }, { link: "http://rooster.html", title: "Rooster" } ] }, { section_title: "Colors", entries: [ { link: "http://Red.html", title: "Red" }, { link: "http://Blue.html", title: "Blue" } ] }, ]

Now that we have the data structure taken care of, we'll put that in our main Vue instance. From there we're going to pass this data structure down to a Vue component. If you haven't already go ahead look at our Vue Components tutorial. This is how we pass dataTree via the props.

<tree-view v-bind:tree="dataTree"> </tree-view>
Vue.component("tree-view", { template: `<div> {{ tree }} </div>`, props: ["tree"] });

You'll notice that the tree output is just a javascript array typecast to a string, at this point. We'll need to do some rendering before it looks pretty. Before you hit the doit button, see if you can figure it for yourself. As you can see above, you can now access the data from "tree". Try using a for loop to iterate across the sections.

This code just iterates across the sections in the tree.

Vue.component("tree-view", { template: `<div> <div v-for="section in tree"> <div class="head"> {{ section.section_title }} </div> {{ section.entries }} </div> </div>`, props: ["tree"] });

Within that for loop, add another for loop to iterate the entry anchors.

Vue.component("tree-view", { template: `<div> <div v-for="section in tree"> <div class="head"> {{ section.section_title }} </div> <a v-for="anchor in section.entries" v-bind:href=""> {{ anchor.title }} </a> </div> </div>`, props: ["tree"] });

Now we can see the section titles and anchors, but they're all mashed together. Let's style them a bit:

a { display: block; padding: 8px; background-color: #f2f2f2; border-top: 1px solid #cccccc; font-size: 12px; text-decoration: none; } .head { background-color: #aaaaaa; color: #555555; padding: 12px; }

Now things are looking how they should be. Let's add some functionality to open and close each section. Add a click handler and some code to toggle between closed and unclosed state.

<div class="head" @click="section.closed = !section.closed;"> {{ section.section_title }} </div>

We need to set the close_css class dynamically based on the state of section.closed

<div v-for="section in tree" v-bind:class=" { closed_css: section.closed }">

Then we add some code to reduce the height of the div when in it's closed state

.closed_css { height: 40px; overflow: hidden; border-bottom: 1px solid #777777; }

Did it work?

I bet it didn't. Why not?

The problem is, those close properties aren't reactive. In order to make them reactive, you'll need to specify them in the beginning, like this:

dataTree: [ { closed: false, section_title: "Animals", entries: [ { link: "http://Crocodile.html", title: "Crocodile" }, { link: "http://rooster.html", title: "Rooster" } ] }, { closed: false, section_title: "Colors", entries: [ { link: "http://Red.html", title: "Red" }, { link: "http://Blue.html", title: "Blue" } ] }, ]

And now, let's make it fold up and down with an animation by adding some CSS state transitions.

.section_css { overflow-y: hidden; max-height: 40px; transition: all 700ms ease; border-bottom: 1px solid #777777; } .open_css { max-height: 130px; }

We'll need to add the section_css class to each top level div.

<div v-for="section in tree" class="section_css" v-bind:class=" { open_css: !section.closed }">

You'll notice, these CSS transitions need 4 things to work:

  • overflow: hidden; When the section is closed, this prevents the content from bleeding through to section below it.
  • transition: all 700ms ease; This is what causes the div to actually close, when open_css is removed.
  • max-height: 40px; This tells the div what height to stop at, at the end of the transition.
  • max-height: 130px; Tells the div where to stop while opening.

The max-height needs to be large enough to accomodate the contents of the div but not so large that it's significantly larger. If it's too large then there will be a delay in the animation as it attempts to close from the too large distance.

Get the Latest Tutorial Updates
<< >>