title: Angular Material mat-tree get checkbox values
body: <p>I'm using <code>Angular Material v6.0</code> MatTreeModule (mat-tree) with checkboxes.
  But I'm having a hard time figuring out how to determine which nodes have been checked and which nodes have not been checked.
  In the example by Angular Material, they give the very good source code to set it up which I was able to set up just fine.</p>
  
  <p>However, I'm unable to determine which checkboxes have been checked and which ones have not.  I've tried for hours trying to figure this out with no luck.</p>
  
  <p>My goal is end-users will check or uncheck the checkboxes in the tree and from there, I need to run some process after they have made their selections.</p>
  
  <p>But I'm completely stuck trying to figure out which mat-tree-nodes are checked and not checked and there's no good working example that I could find anywhere.</p>
  
  <p>The key source code is found <a href="https://stackblitz.com/angular/gabkadkvybq?file=app%2Ftree-checklist-example.html" rel="noreferrer">here</a></p>
  
  <p>More info on mat-tree is found <a href="https://material.angular.io/components/tree/examples" rel="noreferrer">here</a></p>
  
  <p>Can anyone help on how to determine if the checkboxes have been checked?</p>
  
  <p>Thanks.</p>
  
  <p>Per request, I'm adding the code here from the two code files:</p>
  
  <p>app/tree-checklist-example.ts
  app/tree-checklist-example.html</p>
  
  <p>Typescript source code from "app/tree-checklist-example.ts"</p>
  
  <pre><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: object, 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;
      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);
      return descendants.every(child =&gt; this.checklistSelection.isSelected(child));
    }
  
    /** 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);
    }
  
    /** 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);
    }
  }
  
  
  
  HTML source code from "app/tree-checklist-example.html":
  
  &lt;mat-tree [dataSource]="dataSource" [treeControl]="treeControl"&gt;
    &lt;mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding&gt;
      &lt;button mat-icon-button disabled&gt;&lt;/button&gt;
      &lt;mat-checkbox class="checklist-leaf-node"
                    [checked]="checklistSelection.isSelected(node)"
                    (change)="checklistSelection.toggle(node);"&gt;{{node.item}}&lt;/mat-checkbox&gt;
    &lt;/mat-tree-node&gt;
  
    &lt;mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding&gt;
      &lt;button mat-icon-button disabled&gt;&lt;/button&gt;
      &lt;mat-form-field&gt;
        &lt;input matInput #itemValue placeholder="New item..."&gt;
      &lt;/mat-form-field&gt;
      &lt;button mat-button (click)="saveNode(node, itemValue.value)"&gt;Save&lt;/button&gt;
    &lt;/mat-tree-node&gt;
  
    &lt;mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding&gt;
      &lt;button mat-icon-button matTreeNodeToggle
              [attr.aria-label]="'toggle ' + node.filename"&gt;
        &lt;mat-icon class="mat-icon-rtl-mirror"&gt;
          {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
        &lt;/mat-icon&gt;
      &lt;/button&gt;
      &lt;mat-checkbox [checked]="descendantsAllSelected(node)"
                    [indeterminate]="descendantsPartiallySelected(node)"
                    (change)="todoItemSelectionToggle(node)"&gt;{{node.item}}&lt;/mat-checkbox&gt;
      &lt;button mat-icon-button (click)="addNewItem(node)"&gt;&lt;mat-icon&gt;add&lt;/mat-icon&gt;&lt;/button&gt;
    &lt;/mat-tree-node&gt;
  &lt;/mat-tree&gt;
  </code></pre>
  
  <p>As mentioned earlier to see the complete source code and a demo of it working, please go to:
  <a href="https://stackblitz.com/angular/gabkadkvybq?file=app%2Ftree-checklist-example.html" rel="noreferrer">https://stackblitz.com/angular/gabkadkvybq?file=app%2Ftree-checklist-example.html</a></p>
  
  <p>Thanks.</p>