Originally posted on devinduct.com
Quick Introduction
Some people say that recursion is hard to digest. In terms of software development, I don't feel like it is. A simple definition would be that recursive function
is a self-invoking function, meaning that it will invoke itself at a certain point of its execution.
A more theoretical definition would be that recursion
is a behavior requiring two properties:
- A base case - the case which will stop the recursion
- A set of rules responsible for reducing all cases towards the base case
I cannot decide which one is more important. Without the base case, the recursion will become an infinite loop and yet without the reducing cases towards it, we cannot achieve the desired behavior. Anyway, you need both in order to make it function properly.
Recursion and Vue Components
In Vue, recursion is very much possible and quite useful. I mean, not only in Vue, we can follow the rules above to implement recursive behavior in any framework. So, based on the given definition, we can say that a recursive component is a component invoking itself.
When is this useful? Well, whenever you need to use the same template structure, but with hierarchical input data, you can use recursion. Examples are components like tree views for displaying folder structure, comments on your website, nested menus...everything where the parent and the child have the same structure.
Ok, let us build an example to show all of this in practice.
The Problem
Imagine this, you came to work like any other day, made yourself a nice cup of coffee and started reading your favorite blog. Suddenly, your boss comes and says that you need to implement a new page where you will display all folders, subfolders, and files without knowing how many of them will exist. It can show 10, 5 or 100 folders. You start scratching your head thinking about how to solve this, and of course, you think of recursion.
Minimal number of components to solve this is 1, but in our example, we will create two of them:
- The root component
- The folder component
But first, we need to create sample data.
The Data
As mentioned earlier, a recursion comes in handy when we have hierarchically organized data where the child has the same structure as its parent. Here is the data reflecting this:
const root = {
text: 'Root Folder',
leaf: false,
expanded: true,
children: [{
text: 'Sub Folder 1',
leaf: false,
expanded: false,
children: [{
text: 'Sub Sub Folder 1',
leaf: false,
expanded: false,
children: [{
text: 'SomeFile1.js',
leaf: true
}]
}, {
text: 'Sub Sub Folder 2',
leaf: false,
expanded: false,
children: []
}, {
text: 'SomeFile.txt',
leaf: true
}]
}]
}
By having the data above, we are ready to create our components.
The Root Component
This component will be the starting point of our folder tree. It will initiate the rendering of all children, but it can also display some independent information if required since it will not be a part of the recursion itself.
It will contain one property, called folder
for example, to which we will bind our root
data object. This property will be passed on to the child component, which will recursively create the folder tree structure based on it.
Template
<template>
<ul class="folders">
<li>Folders</li>
<folder v-bind:folder="folder"></folder>
</ul>
</template>
The Code
import Folder from './Folder.vue';
export default {
name: 'root',
props: {
folder: Object
},
components: {
Folder
}
};
The Styling
ul.folders {
padding: 1rem;
margin: 0;
box-sizing: border-box;
width: 100%;
list-style: none
}
ul.folders > li:first-child {
padding: 1rem 1rem 1rem 0
}
It's as simple as that.
The Folder component
This component is responsible for rendering each folder in our tree. It will display the information about the current folder and render its children if any. Also, the folders are clickable, and by clicking on one, the component will display its subfolders and files.
Template
<template>
<li class="folder" v-bind:class="[folder.leaf ? 'is-leaf' : 'is-folder']">
<span v-on:click="expand()">{{ folder.text }}</span>
<ul class="sub-folders" v-if="folder.children && folder.children.length > 0" v-show="folder.expanded">
<folder v-for="child in folder.children" v-bind:folder="child"></folder>
</ul>
<div class="folder-empty" v-else v-show="!folder.leaf && folder.expanded">No Data</div>
</li>
</template>
The Code
export default {
name: "folder",
props: {
folder: Object
},
methods: {
expand() {
if (this.folder.leaf) {
return;
}
this.folder.expanded = !this.folder.expanded;
}
}
};
The Styling
li.is-folder {
padding: 1rem;
border-left: 1px solid #d3d3d3;
margin-bottom: 0.5rem
}
li.is-folder > span {
padding: 0.5rem;
border: 1px solid #d3d3d3;
cursor: pointer;
display:inline-block
}
li.is-leaf {
padding: 0 0 0 1rem;
color: #000;
}
ul.sub-folders {
padding: 1rem 1rem 0 0;
margin: 0;
box-sizing: border-box;
width: 100%;
list-style: none
}
div.folder-empty {
padding: 1rem 1rem 0 1rem;
color: #000;
opacity: 0.5
}
Example Usage
In order to use the component you've just created, all you need to do is to import the root
component where this functionality is required and pass in the data structure. For example, on the page your boss requested. The App.vue
component would look something like this:
Template
<template>
<div class="vue-app">
<root v-bind:folder="root"></root>
</div>
</template>
The Code
import Root from './Root.vue';
export default {
name: 'app',
data: function () {
return {
root: {
text: 'Root Folder',
leaf: false,
expanded: true,
children: [{
text: 'Sub Folder 1',
leaf: false,
expanded: false,
children: [{
text: 'Sub Sub Folder 1',
leaf: false,
expanded: false,
children: [{
text: 'SomeFile1.js',
leaf: true
}]
}, {
text: 'Sub Sub Folder 2',
leaf: false,
expanded: false,
children: []
}, {
text: 'SomeFile.txt',
leaf: true
}]
}]
}
}
},
components: {
Root
}
};
And that's it! Your page is ready to display as many folders as it receives. Check out the live example on codepen here.
Wrapping Up
Recursion is not that hard as it looks. It's simple execution of the same code block over and over again with different input parameters until it reaches the base case.
I hope this article will provide a better understanding of recursion and how to create a recursive component with Vue.
Thank you for reading and see you in the next post.
Top comments (6)
Nice explanation Milos, i actually use this in a project without realizing it haha. A small tip/pointer if you don't mind would to be add a demo in the end of the article in the form of codesandbox.io or codepen.io (i prefer the first myself) to make it even more visual to others.
ps: Loved Novi Sad, went there last year for the Exit Festival :)
Thanks, I appreciate it! Yes, I agree, and my articles mostly do contain a fiddle at the end. Don't know why I didn't do it for this one.
:) Glad to hear that you loved Novi Sad. Are you planning on visiting again? Did you like the Petrovaradin fortress?
People should be cautious though, almost every year someone falls off of it during the festival, unfortunately.
I am not sure about visiting it again.. at least not the festival i would say. It is a long trip and the fact that it starts really late and ends early in the morning combined with the heat in your tent.. maybe i'll need to book a room next time. The fortress was really impressive i must say, especially at sunrise the view was really beautiful.
Yes i can imagine that the combination of alcohol (and maybe drugs?) with heights is not the best... My only complaint was that cab drivers try to scam you but i guess that will happen in every city and country.
Yes, I understand. Sometimes it can get pretty hot over here. I love going up there, at any time. You can see the whole city!
The people are usually intoxicated when they fall over the wall and about the cab drivers, they try to scam the local people, I can imagine what do they try to do when someone is not from around here.
I've added a codepen implementation for this post :)
P.S Couldn't embed it because dev.to was throwing an error. Will try again later.
Stumbled upon this article and I love it! You gave a great explanation that's so easy to follow.