title: Angular Material Tree Add Child Button to Every Node
body: <p><a href="https://stackblitz.com/angular/qkjdynkykodo?file=src%2Fapp%2Ftree-checklist-example.html" rel="nofollow noreferrer">https://stackblitz.com/angular/qkjdynkykodo?file=src%2Fapp%2Ftree-checklist-example.html</a></p>
  <p>in this sample, &quot;add child button&quot; only show at parent node, I want to show add button at all child and parent node so I can create new levels for all nodes not only parent nodes.</p>
  <p><a href="https://i.stack.imgur.com/ywkXS.jpg" rel="nofollow noreferrer">sample output image at link</a></p>
  <p><a href="https://i.stack.imgur.com/S98Hu.jpg" rel="nofollow noreferrer">What i want and transform this sample to my requirement</a></p>
  <pre><code>My Html
  &lt;mat-tree [dataSource]=&quot;dataSource&quot; [treeControl]=&quot;treeControl&quot;&gt;
    &lt;mat-tree-node *matTreeNodeDef=&quot;let node&quot; matTreeNodeToggle matTreeNodePadding&gt;
      &lt;button mat-icon-button disabled&gt;&lt;/button&gt;
      &lt;mat-checkbox class=&quot;checklist-leaf-node&quot;
                    [checked]=&quot;checklistSelection.isSelected(node)&quot;
                    (change)=&quot;todoLeafItemSelectionToggle(node)&quot;&gt;{{node.item}}&lt;/mat-checkbox&gt;
                       &lt;button mat-icon-button (click)=&quot;addNewItem(node)&quot;&gt;&lt;mat-icon&gt;add&lt;/mat-icon&gt;&lt;/button&gt;
    &lt;/mat-tree-node&gt;
  
    &lt;mat-tree-node *matTreeNodeDef=&quot;let node; when: hasNoContent&quot; matTreeNodePadding&gt;
      &lt;button mat-icon-button disabled&gt;&lt;/button&gt;
      &lt;mat-form-field&gt;
        &lt;mat-label&gt;New item...&lt;/mat-label&gt;
        &lt;input matInput #itemValue placeholder=&quot;Ex. Lettuce&quot;&gt;
      &lt;/mat-form-field&gt;
      &lt;button mat-button (click)=&quot;saveNode(node, itemValue.value)&quot;&gt;Save&lt;/button&gt;
    &lt;/mat-tree-node&gt;
  
    &lt;mat-tree-node *matTreeNodeDef=&quot;let node; when: hasChild&quot; matTreeNodePadding&gt;
      &lt;button mat-icon-button matTreeNodeToggle
              [attr.aria-label]=&quot;'Toggle ' + node.item&quot;&gt;
        &lt;mat-icon class=&quot;mat-icon-rtl-mirror&quot;&gt;
          {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
        &lt;/mat-icon&gt;
      &lt;/button&gt;
      &lt;mat-checkbox [checked]=&quot;descendantsAllSelected(node)&quot;
                    [indeterminate]=&quot;descendantsPartiallySelected(node)&quot;
                    (change)=&quot;todoItemSelectionToggle(node)&quot;&gt;{{node.item}}&lt;/mat-checkbox&gt;
      &lt;button mat-icon-button (click)=&quot;addNewItem(node)&quot;&gt;&lt;mat-icon&gt;add&lt;/mat-icon&gt;&lt;/button&gt;
    &lt;/mat-tree-node&gt;
  &lt;/mat-tree&gt;
  
  
  &lt;!-- Copyright 2020 Google LLC. All Rights Reserved.
      Use of this source code is governed by an MIT-style license that
      can be found in the LICENSE file at http://angular.io/license --&gt;
  
  My TS Code
  import {SelectionModel} from '@angular/cdk/collections';
  import {FlatTreeControl} from '@angular/cdk/tree';
  import {Component, Injectable} from '@angular/core';
  import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
  import {BehaviorSubject} from 'rxjs';
  
  /**
   * Node for to-do item
   */
  export class TodoItemNode {
    children: TodoItemNode[];
    item: string;
  }
  
  /** Flat to-do item node with expandable and level information */
  export class TodoItemFlatNode {
    item: string;
    level: number;
    expandable: boolean;
  }
  
  /**
   * The Json object for to-do list data.
   */
  const TREE_DATA = {
    Groceries: {
      'Almond Meal flour': null,
      'Organic eggs': null,
      'Protein Powder': null,
      Fruits: {
        Apple: null,
        Berries: ['Blueberry', 'Raspberry'],
        Orange: null
      }
    },
    Reminders: [
      'Cook dinner',
      'Read the Material Design spec',
      'Upgrade Application to Angular'
    ]
  };
  
  /**
   * Checklist database, it can build a tree structured Json object.
   * Each node in Json object represents a to-do item or a category.
   * If a node is a category, it has children items and new items can be added under the category.
   */
  @Injectable()
  export class ChecklistDatabase {
    dataChange = new BehaviorSubject&lt;TodoItemNode[]&gt;([]);
  
    get data(): TodoItemNode[] { return this.dataChange.value; }
  
    constructor() {
      this.initialize();
    }
  
    initialize() {
      // Build the tree nodes from Json object. The result is a list of `TodoItemNode` with nested
      //     file node as children.
      const data = this.buildFileTree(TREE_DATA, 0);
  
      // Notify the change.
      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 `TodoItemNode`.
     */
    buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
      return Object.keys(obj).reduce&lt;TodoItemNode[]&gt;((accumulator, key) =&gt; {
        const value = obj[key];
        const node = new TodoItemNode();
        node.item = key;
  
        if (value != null) {
          if (typeof value === 'object') {
            node.children = this.buildFileTree(value, level + 1);
          } else {
            node.item = value;
          }
        }
  
        return accumulator.concat(node);
      }, []);
    }
  
    /** Add an item to to-do list */
    insertItem(parent: TodoItemNode, name: string) {
      if (parent.children) {
        parent.children.push({item: name} as TodoItemNode);
        this.dataChange.next(this.data);
      }
    }
  
    updateItem(node: TodoItemNode, name: string) {
      node.item = name;
      this.dataChange.next(this.data);
    }
  }
  
  /**
   * @title Tree with checkboxes
   */
  @Component({
    selector: 'tree-checklist-example',
    templateUrl: 'tree-checklist-example.html',
    styleUrls: ['tree-checklist-example.css'],
    providers: [ChecklistDatabase]
  })
  export class TreeChecklistExample {
    /** Map from flat node to nested node. This helps us finding the nested node to be modified */
    flatNodeMap = new Map&lt;TodoItemFlatNode, TodoItemNode&gt;();
  
    /** Map from nested node to flattened node. This helps us to keep the same object for selection */
    nestedNodeMap = new Map&lt;TodoItemNode, TodoItemFlatNode&gt;();
  
    /** A selected parent node to be inserted */
    selectedParent: TodoItemFlatNode | null = null;
  
    /** The new item's name */
    newItemName = '';
  
    treeControl: FlatTreeControl&lt;TodoItemFlatNode&gt;;
  
    treeFlattener: MatTreeFlattener&lt;TodoItemNode, TodoItemFlatNode&gt;;
  
    dataSource: MatTreeFlatDataSource&lt;TodoItemNode, TodoItemFlatNode&gt;;
  
    /** The selection for checklist */
    checklistSelection = new SelectionModel&lt;TodoItemFlatNode&gt;(true /* multiple */);
  
    constructor(private _database: ChecklistDatabase) {
      this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel,
        this.isExpandable, this.getChildren);
      this.treeControl = new FlatTreeControl&lt;TodoItemFlatNode&gt;(this.getLevel, this.isExpandable);
      this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
  
      _database.dataChange.subscribe(data =&gt; {
        this.dataSource.data = data;
      });
    }
  
    getLevel = (node: TodoItemFlatNode) =&gt; node.level;
  
    isExpandable = (node: TodoItemFlatNode) =&gt; node.expandable;
  
    getChildren = (node: TodoItemNode): TodoItemNode[] =&gt; node.children;
  
    hasChild = (_: number, _nodeData: TodoItemFlatNode) =&gt; _nodeData.expandable;
  
    hasNoContent = (_: number, _nodeData: TodoItemFlatNode) =&gt; _nodeData.item === '';
  
    /**
     * Transformer to convert nested node to flat node. Record the nodes in maps for later use.
     */
    transformer = (node: TodoItemNode, level: number) =&gt; {
      const existingNode = this.nestedNodeMap.get(node);
      const flatNode = existingNode &amp;&amp; existingNode.item === node.item
          ? existingNode
          : new TodoItemFlatNode();
      flatNode.item = node.item;
      flatNode.level = level;
      flatNode.expandable = !!node.children?.length;
      this.flatNodeMap.set(flatNode, node);
      this.nestedNodeMap.set(node, flatNode);
      return flatNode;
    }
  
    /** Whether all the descendants of the node are selected. */
    descendantsAllSelected(node: TodoItemFlatNode): boolean {
      const descendants = this.treeControl.getDescendants(node);
      const descAllSelected = descendants.length &gt; 0 &amp;&amp; descendants.every(child =&gt; {
        return this.checklistSelection.isSelected(child);
      });
      return descAllSelected;
    }
  
    /** Whether part of the descendants are selected */
    descendantsPartiallySelected(node: TodoItemFlatNode): boolean {
      const descendants = this.treeControl.getDescendants(node);
      const result = descendants.some(child =&gt; this.checklistSelection.isSelected(child));
      return result &amp;&amp; !this.descendantsAllSelected(node);
    }
  
    /** Toggle the to-do item selection. Select/deselect all the descendants node */
    todoItemSelectionToggle(node: TodoItemFlatNode): void {
      this.checklistSelection.toggle(node);
      const descendants = this.treeControl.getDescendants(node);
      this.checklistSelection.isSelected(node)
        ? this.checklistSelection.select(...descendants)
        : this.checklistSelection.deselect(...descendants);
  
      // Force update for the parent
      descendants.forEach(child =&gt; this.checklistSelection.isSelected(child));
      this.checkAllParentsSelection(node);
    }
  
    /** Toggle a leaf to-do item selection. Check all the parents to see if they changed */
    todoLeafItemSelectionToggle(node: TodoItemFlatNode): void {
      this.checklistSelection.toggle(node);
      this.checkAllParentsSelection(node);
    }
  
    /* Checks all the parents when a leaf node is selected/unselected */
    checkAllParentsSelection(node: TodoItemFlatNode): void {
      let parent: TodoItemFlatNode | null = this.getParentNode(node);
      while (parent) {
        this.checkRootNodeSelection(parent);
        parent = this.getParentNode(parent);
      }
    }
  
    /** Check root node checked state and change it accordingly */
    checkRootNodeSelection(node: TodoItemFlatNode): void {
      const nodeSelected = this.checklistSelection.isSelected(node);
      const descendants = this.treeControl.getDescendants(node);
      const descAllSelected = descendants.length &gt; 0 &amp;&amp; descendants.every(child =&gt; {
        return this.checklistSelection.isSelected(child);
      });
      if (nodeSelected &amp;&amp; !descAllSelected) {
        this.checklistSelection.deselect(node);
      } else if (!nodeSelected &amp;&amp; descAllSelected) {
        this.checklistSelection.select(node);
      }
    }
  
    /* Get the parent node of a node */
    getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null {
      const currentLevel = this.getLevel(node);
  
      if (currentLevel &lt; 1) {
        return null;
      }
  
      const startIndex = this.treeControl.dataNodes.indexOf(node) - 1;
  
      for (let i = startIndex; i &gt;= 0; i--) {
        const currentNode = this.treeControl.dataNodes[i];
  
        if (this.getLevel(currentNode) &lt; currentLevel) {
          return currentNode;
        }
      }
      return null;
    }
  
    /** Select the category so we can insert the new item. */
    addNewItem(node: TodoItemFlatNode) {
      const parentNode = this.flatNodeMap.get(node);
      this._database.insertItem(parentNode!, '');
      this.treeControl.expand(node);
    }
  
    /** Save the node to database */
    saveNode(node: TodoItemFlatNode, itemValue: string) {
      const nestedNode = this.flatNodeMap.get(node);
      this._database.updateItem(nestedNode!, itemValue);
    }
  }
  
  
  /**  Copyright 2020 Google LLC. All Rights Reserved.
      Use of this source code is governed by an MIT-style license that
      can be found in the LICENSE file at http://angular.io/license */
  </code></pre>
  <p>Thanks</p>