0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Angular Material 動的Tree 導入

Last updated at Posted at 2022-06-21

動的Tree導入後イメージ

image

image

image

この記事の目的

  • Angular Material のDynamic Treeを組み込みます。

👇これより先は下記記事の内容を前提とします

コンポーネント追加

ng generate component tree-sample

コンポーネント組み込み

app.module.ts

app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { HttpClientModule } from '@angular/common/http';

import { MaterialModule } from '../material.module';
import { TreeSampleComponent } from './tree-sample/tree-sample.component';

@NgModule({
  declarations: [
    AppComponent,
    TreeSampleComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MaterialModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

app.component.html
<app-tree-sample></app-tree-sample>

コンポーネント定義

tree-sample.component.css

tree-sample.component.css
.example-tree-progress-bar {
  margin-left: 30px;
}
.type-icon {
  color: #757575;
  margin-right: 5px;
}

tree-sample.component.ts

tree-sample.component.ts
import {CollectionViewer, SelectionChange, DataSource} from '@angular/cdk/collections';
import {FlatTreeControl} from '@angular/cdk/tree';
import {Component, Injectable } from '@angular/core';
import {BehaviorSubject, merge, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';

export class DynamicFlatNode {
  constructor(
    public item: string,
    public id: string,
    public level = 1,
    public type = 'file',
    public expandable = false,
    public isLoading = false,
  ) {}
}

@Injectable({providedIn: 'root'})
export class DynamicDatabase {

  topNodes = [
    {name: 'root',type: 'folder'},
];

  initialData(): DynamicFlatNode[] {
    return this.topNodes.map(tn => {
      if(tn.type=='file'){
        return new DynamicFlatNode(tn.name,'', 0, tn.type, false);
      }else{
        return new DynamicFlatNode(tn.name,'', 0, tn.type, true);
      }
    }
    );
  }

}

export class DynamicDataSource implements DataSource<DynamicFlatNode> {
  dataChange = new BehaviorSubject<DynamicFlatNode[]>([]);

  get data(): DynamicFlatNode[] {
    return this.dataChange.value;
  }
  set data(value: DynamicFlatNode[]) {
    this._treeControl.dataNodes = value;
    this.dataChange.next(value);
  }

  constructor(
    private _treeControl: FlatTreeControl<DynamicFlatNode>,
    private _database: DynamicDatabase,
    private http: HttpClient,
  ) {}

  connect(collectionViewer: CollectionViewer): Observable<DynamicFlatNode[]> {
    this._treeControl.expansionModel.changed.subscribe(change => {
      if (
        (change as SelectionChange<DynamicFlatNode>).added ||
        (change as SelectionChange<DynamicFlatNode>).removed
      ) {
        this.handleTreeControl(change as SelectionChange<DynamicFlatNode>);
      }
    });

    return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
  }

  disconnect(collectionViewer: CollectionViewer): void {}

  handleTreeControl(change: SelectionChange<DynamicFlatNode>) {
    if (change.added) {
      change.added.forEach(node => this.toggleNode(node, true));
    }
    if (change.removed) {
      change.removed
        .slice()
        .reverse()
        .forEach(node => this.toggleNode(node, false));
    }
  }

  toggleNode(node: DynamicFlatNode, expand: boolean) {
    const index = this.data.indexOf(node);

    if (expand) {

      node.isLoading = true;

      setTimeout(() => {

        let childNodes = [
          {name: 'under01',type: 'folder'},
          {name: 'under02',type: 'folder'},
          {name: 'under_file01',type: 'file'},
          {name: 'under_file02',type: 'file'},
        ];

        const nodes = childNodes.map(
          cn => {
            if(cn.type=='file'){
              return new DynamicFlatNode(cn.name,"0", node.level + 1, cn.type, false);
            }else{
              return new DynamicFlatNode(cn.name,"0", node.level + 1, cn.type, true);
            }
          }
        );

        this.data.splice(index + 1, 0, ...nodes);

        // notify the change
        this.dataChange.next(this.data);
        node.isLoading = false;
      }, 1000);

    }else{

      let count = 0;
      for (
        let i = index + 1;
        i < this.data.length && this.data[i].level > node.level;
        i++, count++
      ) {}
      this.data.splice(index + 1, count);

      this.dataChange.next(this.data);

    }

  }
}

@Component({
  selector: 'app-tree-sample',
  templateUrl: './tree-sample.component.html',
  styleUrls: ['./tree-sample.component.css']
})
export class TreeSampleComponent{

  constructor(
    database: DynamicDatabase,
    private http: HttpClient,
  ) {
  this.treeControl = new FlatTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandable);
  this.dataSource = new DynamicDataSource(this.treeControl, database,http);

  this.dataSource.data = database.initialData();

  }

  treeControl: FlatTreeControl<DynamicFlatNode>;
  dataSource: DynamicDataSource;
  getLevel = (node: DynamicFlatNode) => node.level;
  isExpandable = (node: DynamicFlatNode) => node.expandable;
  hasChild = (_: number, _nodeData: DynamicFlatNode) => _nodeData.expandable;

}

tree-sample.component.html

tree-sample.component.html
<p>tree-sample works!</p>
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
  <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
    <button mat-icon-button disabled></button>
    <mat-icon class="type-icon" [attr.aria-label]="node.type + 'icon'">
      {{ node.type ==='file' ? 'description' : 'folder' }}
    </mat-icon>
    {{node.item}}
  </mat-tree-node>
  <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
    <button mat-icon-button
            [attr.aria-label]="'Toggle ' + node.item" matTreeNodeToggle>
      <mat-icon class="mat-icon-rtl-mirror">
        {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
      </mat-icon>
    </button>
    <mat-radio-button value="{{node.name}}" >
    <mat-icon class="type-icon" [attr.aria-label]="node.type + 'icon'">
      {{ node.type ==='file' ? 'description' : 'folder' }}
    </mat-icon>
    {{node.item}}
    </mat-radio-button>
    <mat-progress-bar *ngIf="node.isLoading"
                      mode="indeterminate"
                      class="example-tree-progress-bar"></mat-progress-bar>
  </mat-tree-node>
</mat-tree>

動作確認

ng s --o

👇前提記事

👇関連記事

👇GitHubはこちら

👇参考URL

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?