import { FlatTreeControl } from '@angular/cdk/tree' ; import { Component , Injectable } from '@angular/core' ; import { MatTreeFlatDataSource , MatTreeFlattener } from '@angular/material/tree' ; import { BehaviorSubject , Observable , of as observableOf } from 'rxjs' ; /**
 * File node data with nested structure.
 * Each node has a filename, and a type or a list of children.
 */ export class FileNode { children : FileNode [ ] ; filename : string ; type : any ; } /** Flat node with expandable and level information */ export class FileFlatNode { constructor ( public expandable , public filename , public level , public type ) { } } /**
 * The file structure tree data in string. The data could be parsed into a Json object
 */ const TREE_DATA = JSON . stringify ( { Applications : { Calendar : 'app' , Chrome : 'app' , Webstorm : 'app' } , Documents : { angular : { src : { compiler : 'ts' , core : 'ts' } } , material2 : { src : { button : 'ts' , checkbox : 'ts' , input : 'ts' } } } , Downloads : { October : 'pdf' , November : 'pdf' , Tutorial : 'html' } , Pictures : { 'Photo Booth Library' : { Contents : 'dir' , Pictures : 'dir' } , Sun : 'png' , Woods : 'jpg' } } ) ; /**
 * File database, it can build a tree structured Json object from string.
 * Each node in Json object represents a file or a directory. For a file, it has filename and type.
 * For a directory, it has filename and children (a list of files or directories).
 * The input will be a json object string, and the output is a list of `FileNode` with nested
 * structure.
 */ @ Injectable ( ) export class FileDatabase { dataChange = new BehaviorSubject < FileNode [ ] > ( [ ] ) ; get data ( ) : FileNode [ ] { return this . dataChange . value ; } constructor ( ) { this . initialize ( ) ; } initialize ( ) { const dataObject = JSON . parse ( TREE_DATA ) ; const data = this . buildFileTree ( dataObject , 0 ) ; this . dataChange . next ( data ) ; } /**
     * Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
     * The return value is the list of `FileNode`.
     */ buildFileTree ( obj , level ) { return Object . keys ( obj ) . reduce < FileNode [ ] > ( ( accumulator , key ) => { const value = obj [ key ] ; const node = new FileNode ( ) ; node . filename = key ; if ( value != null ) { if ( typeof value === 'object' ) { node . children = this . buildFileTree ( value , level + 1 ) ; } else { node . type = value ; } } return accumulator . concat ( node ) ; } , [ ] ) ; } } title @ Component ( { selector : 'tree-flat-overview-example' , templateUrl : 'tree-flat-overview-example.html' , styleUrls : [ 'tree-flat-overview-example.css' ] , providers : [ FileDatabase ] } ) export class TreeFlatOverviewExample { treeControl : FlatTreeControl < FileFlatNode > ; treeFlattener : MatTreeFlattener < FileNode , FileFlatNode > ; dataSource : MatTreeFlatDataSource < FileNode , FileFlatNode > ; constructor ( database ) { this . treeFlattener = new MatTreeFlattener ( this . transformer , this . _getLevel , this . _isExpandable , this . _getChildren ) ; this . treeControl = new FlatTreeControl < FileFlatNode > ( this . _getLevel , this . _isExpandable ) ; this . dataSource = new MatTreeFlatDataSource ( this . treeControl , this . treeFlattener ) ; database . dataChange . subscribe ( data => this . dataSource . data = data ) ; } transformer = ( node , level ) => { return new FileFlatNode ( ! ! node . children , node . filename , level , node . type ) ; } ; private _getLevel = ( node ) => node . level ; private _isExpandable = ( node ) => node . expandable ; private _getChildren = ( node ) : Observable < FileNode [ ] > => observableOf ( node . children ) ; hasChild = ( _ , _nodeData ) => _nodeData . expandable ; }