license import { FlatTreeControl } from '@angular/cdk/tree' ; import { Component , Injectable } from '@angular/core' ; import { MatTreeFlatDataSource , MatTreeFlattener } from '@angular/material/tree' ; import { BehaviorSubject , Observable } from 'rxjs' ; const LOAD_MORE = 'LOAD_MORE' ; /** Nested node */ export class LoadmoreNode { childrenChange = new BehaviorSubject < LoadmoreNode [ ] > ( [ ] ) ; get children ( ) : LoadmoreNode [ ] { return this . childrenChange . value ; } constructor ( public item , public hasChildren = false , public loadMoreParentItem = null ) { } } /** Flat node with expandable and level information */ export class LoadmoreFlatNode { constructor ( public item , public level = 1 , public expandable = false , public loadMoreParentItem = null ) { } } /**
 * A database that only load part of the data initially. After user clicks on the `Load more`
 * button, more data will be loaded.
 */ @ Injectable ( ) export class LoadmoreDatabase { batchNumber = 5 ; dataChange = new BehaviorSubject < LoadmoreNode [ ] > ( [ ] ) ; nodeMap = new Map < string , LoadmoreNode > ( ) ; /** The data */ rootLevelNodes : string [ ] = [ 'Vegetables' , 'Fruits' ] ; dataMap = new Map < string , string [ ] > ( [ [ 'Fruits' , [ 'Apple' , 'Orange' , 'Banana' ] ] , [ 'Vegetables' , [ 'Tomato' , 'Potato' , 'Onion' ] ] , [ 'Apple' , [ 'Fuji' , 'Macintosh' ] ] , [ 'Onion' , [ 'Yellow' , 'White' , 'Purple' , 'Green' , 'Shallot' , 'Sweet' , 'Red' , 'Leek' ] ] , ] ) ; initialize ( ) { const data = this . rootLevelNodes . map ( name => this . _generateNode ( name ) ) ; this . dataChange . next ( data ) ; } /** Expand a node whose children are not loaded */ loadMore ( item , onlyFirstTime = false ) { if ( ! this . nodeMap . has ( item ) || ! this . dataMap . has ( item ) ) { return ; } const parent = this . nodeMap . get ( item ) ! ; const children = this . dataMap . get ( item ) ! ; if ( onlyFirstTime && parent . children ! . length > 0 ) { return ; } const newChildrenNumber = parent . children ! . length + this . batchNumber ; const nodes = children . slice ( 0 , newChildrenNumber ) . map ( name => this . _generateNode ( name ) ) ; if ( newChildrenNumber < children . length ) { nodes . push ( new LoadmoreNode ( LOAD_MORE , false , item ) ) ; } parent . childrenChange . next ( nodes ) ; this . dataChange . next ( this . dataChange . value ) ; } private _generateNode ( item ) { if ( this . nodeMap . has ( item ) ) { return this . nodeMap . get ( item ) ! ; } const result = new LoadmoreNode ( item , this . dataMap . has ( item ) ) ; this . nodeMap . set ( item , result ) ; return result ; } } title @ Component ( { selector : 'tree-loadmore-example' , templateUrl : 'tree-loadmore-example.html' , styleUrls : [ 'tree-loadmore-example.css' ] , providers : [ LoadmoreDatabase ] } ) export class TreeLoadmoreExample { nodeMap = new Map < string , LoadmoreFlatNode > ( ) ; treeControl : FlatTreeControl < LoadmoreFlatNode > ; treeFlattener : MatTreeFlattener < LoadmoreNode , LoadmoreFlatNode > ; dataSource : MatTreeFlatDataSource < LoadmoreNode , LoadmoreFlatNode > ; constructor ( private database ) { this . treeFlattener = new MatTreeFlattener ( this . transformer , this . getLevel , this . isExpandable , this . getChildren ) ; this . treeControl = new FlatTreeControl < LoadmoreFlatNode > ( this . getLevel , this . isExpandable ) ; this . dataSource = new MatTreeFlatDataSource ( this . treeControl , this . treeFlattener ) ; database . dataChange . subscribe ( data => { this . dataSource . data = data ; } ) ; database . initialize ( ) ; } getChildren = ( node ) : Observable < LoadmoreNode [ ] > => node . childrenChange ; transformer = ( node , level ) => { const existingNode = this . nodeMap . get ( node . item ) ; if ( existingNode ) { return existingNode ; } const newNode = new LoadmoreFlatNode ( node . item , level , node . hasChildren , node . loadMoreParentItem ) ; this . nodeMap . set ( node . item , newNode ) ; return newNode ; } ; getLevel = ( node ) => node . level ; isExpandable = ( node ) => node . expandable ; hasChild = ( _ , _nodeData ) => _nodeData . expandable ; isLoadMore = ( _ , _nodeData ) => _nodeData . item === LOAD_MORE ; /** Load more nodes from data source */ loadMore ( item ) { this . database . loadMore ( item ) ; } loadChildren ( node ) { this . database . loadMore ( node . item , true ) ; } }