Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

Recursive TreeView in Angular

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Sat May 30 2015

In this post I will demonstrate how to create a simple treeview using Angular and TypeScript.

Update

I have also created a lazy loaded treview that you can check out here.

TypeScript is optional, but seems to be where Angular is headed, so why not embrace it form the start? In my opinion TypeScript adds some sorely needed semantics to JavaScript.

Anyway, on to the task at hand!

I created a super basic treeview, but it does highlight some core Angular principles by showing how to render (recursive) templates, add click handlers and basic hide/show logic. In reality, this is probably an 80% use case for most Angular development.

I start out by creating a wrapper component that bootstraps the application and includes the actual treeview component on the page. This sample treeview renders a recursive directory structure with sub directories and files.

app.ts

import {Component} from '@angular/core'; import {bootstrap} from '@angular/platform-browser-dynamic'; import{TreeView} from './tree-view'; import {Directory} from './directory'; @Component({ selector: 'my-tree-view', template: '<tree-view [directories]="directories"></tree-view>' }) class MyDemoApp { directories: Array; constructor() { let fall2014 = new Directory('Fall 2014',[],['image1.jpg','image2.jpg','image3.jpg']); let summer2014 = new Directory('Summer 2014',[],['image10.jpg','image20.jpg','image30.jpg']); let pics = new Directory('Pictures',[summer2014,fall2014],[]); let music = new Directory('Music',[],['song1.mp3','song2.mp3']); this.directories = [pics,music]; } } bootstrap(MyDemoApp);

I won't go into a lot of details about the specific component syntax since the Angular team has already released pretty good references and samples.

Next I have created the recursive treeview component

tree-view.ts

import {Component, Input} from '@angular/core'; import {Directory} from './directory'; @Component({ selector: 'tree-view', templateUrl: './components/tree-view/tree-view.html' }) export class TreeView { @Input() directories: Array<Directory>; }

The treeview component is included in the main component as <tree-view></tree-view>, but notice in the html for the treeview, there is a self reference. This is important since it's how I am able to render the nodes recursively.

Tree-view.html

<ul> <li *ngFor="let dir of directories"> <span><input type="checkbox" [checked]="dir.checked" (click)="dir.check()" /></span> <span (click)="dir.toggle()">{{ dir.name }}</span> <div *ngIf="dir.expanded"> <ul > <li *ngFor="let file of dir.files">{{file}}</li> </ul> <tree-view [directories]="dir.directories"></tree-view> </div> </li> </ul>

The final piece is an object model for defining the directory structure that gets bound to the treeview

directory.ts

export class Directory{ name: string; directories: Array; files: Array; expanded:boolean; checked:boolean; constructor(name,directories,files) { this.name = name; this.files = files; this.directories = directories; this.expanded = false; this.checked = false; } toggle(){ this.expanded = !this.expanded; } check(){ let newState = !this.checked; this.checked = newState; this.checkRecursive(newState); } checkRecursive(state){ this.directories.forEach(d => { d.checked = state; d.checkRecursive(state); }) } }

If you're still reading you may have noticed that I purposely define most of the logic in the model class – not in the angular component.

This is conscious choice since it allows me to decouple the core logic from a having a hard dependency on Angular or any other framework for that matter. In the end this is just pure typescript that can be ported to just about any other framework (e.g. Aurelia, Angular 1.x, ReactJS, whatever..).

You may find it odd that I advocate that you limit the use of Angular in an Angular tutorial. However, this is not meant as a criticism of Angular, but rather a consequence of how fluid the JavaScript framework situation is at the moment. Keeping things decoupled and limit unnecessary dependencies is important if you want to avoid pain during a future migration.

Another huge benefit of this design is that it's very unit test friendly. A unit test doesn't even have to know that a UI framework is involved.

Here is a screen shot of an unstyled version of the treeview

Here is a live demo.

The code is also available on GitHub, so feel free to check it out.

I also have an article describing a lazy loaded treeview here.

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter