From eca35f98ad6eb0bcf29f1bb0e67313e26e156f45 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 9 Oct 2018 23:06:25 +0200 Subject: [PATCH 1/4] docs(tree-checklist-example): root tree node correct selected state Fixed the checked state of the root node in the tree when the initial root state was checked, but then children were deselected, causing the root node to not update correctly. Fixes #13314 --- .../tree-checklist/tree-checklist-example.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/material-examples/tree-checklist/tree-checklist-example.ts b/src/material-examples/tree-checklist/tree-checklist-example.ts index 8d91541e9add..6dcc358dea53 100644 --- a/src/material-examples/tree-checklist/tree-checklist-example.ts +++ b/src/material-examples/tree-checklist/tree-checklist-example.ts @@ -168,10 +168,19 @@ export class TreeChecklistExample { return flatNode; } - /** Whether all the descendants of the node are selected */ + /** + * Whether all the descendants of the node are selected. + * And remove the node from this.checklistSelection if it isn't selected. + */ descendantsAllSelected(node: TodoItemFlatNode): boolean { const descendants = this.treeControl.getDescendants(node); - return descendants.every(child => this.checklistSelection.isSelected(child)); + const descAllSelected = descendants.every(child => + this.checklistSelection.isSelected(child) + ); + if (this.checklistSelection.isSelected(node) && !descAllSelected) { + this.checklistSelection.deselect(node); + } + return descAllSelected; } /** Whether part of the descendants are selected */ From 3a6e5946ef70757986a50717bd09cc2e0c012570 Mon Sep 17 00:00:00 2001 From: fabioloreggian <31480768+fabioloreggian@users.noreply.github.com> Date: Wed, 10 Oct 2018 08:37:44 +0200 Subject: [PATCH 2/4] docs(tree-checklist-example): Fix scenario where root node didn't update when children updated When all the children of a root node was selected the root node wasn't updated to selected correctly --- src/material-examples/tree-checklist/tree-checklist-example.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/material-examples/tree-checklist/tree-checklist-example.ts b/src/material-examples/tree-checklist/tree-checklist-example.ts index 6dcc358dea53..0a9b9815ddac 100644 --- a/src/material-examples/tree-checklist/tree-checklist-example.ts +++ b/src/material-examples/tree-checklist/tree-checklist-example.ts @@ -179,6 +179,8 @@ export class TreeChecklistExample { ); if (this.checklistSelection.isSelected(node) && !descAllSelected) { this.checklistSelection.deselect(node); + } else if(!this.checklistSelection.isSelected(node) && descAllSelected) { + this.checklistSelection.select(node); } return descAllSelected; } From 663737bb7441e27b4a5eaeccac6ba2d563a2ef65 Mon Sep 17 00:00:00 2001 From: Fabio Loreggian Date: Wed, 10 Oct 2018 15:18:15 +0200 Subject: [PATCH 3/4] docs(tree-checklist-example): Parent node updates correctly. Root node correctly changes selected state based on children changing selected state --- .../tree-checklist/tree-checklist-example.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/material-examples/tree-checklist/tree-checklist-example.ts b/src/material-examples/tree-checklist/tree-checklist-example.ts index 0a9b9815ddac..dc2ddcd93369 100644 --- a/src/material-examples/tree-checklist/tree-checklist-example.ts +++ b/src/material-examples/tree-checklist/tree-checklist-example.ts @@ -168,20 +168,13 @@ export class TreeChecklistExample { return flatNode; } - /** - * Whether all the descendants of the node are selected. - * And remove the node from this.checklistSelection if it isn't selected. - */ + /** Whether all the descendants of the node are selected. */ descendantsAllSelected(node: TodoItemFlatNode): boolean { const descendants = this.treeControl.getDescendants(node); const descAllSelected = descendants.every(child => this.checklistSelection.isSelected(child) ); - if (this.checklistSelection.isSelected(node) && !descAllSelected) { - this.checklistSelection.deselect(node); - } else if(!this.checklistSelection.isSelected(node) && descAllSelected) { - this.checklistSelection.select(node); - } + this.checkRootNodeSelection(node, descAllSelected); return descAllSelected; } @@ -201,6 +194,16 @@ export class TreeChecklistExample { : this.checklistSelection.deselect(...descendants); } + /** Check root node checked state and change it accordingly */ + checkRootNodeSelection(node: TodoItemFlatNode, descAllSelected: boolean): void { + const nodeSelected = this.checklistSelection.isSelected(node); + if (nodeSelected && !descAllSelected) { + this.checklistSelection.deselect(node); + } else if (!nodeSelected && descAllSelected) { + this.checklistSelection.select(node); + } + } + /** Select the category so we can insert the new item. */ addNewItem(node: TodoItemFlatNode) { const parentNode = this.flatNodeMap.get(node); From a5a94cb7e99cb31d0c4ea460c3cc94919544227c Mon Sep 17 00:00:00 2001 From: Fabio Loreggian Date: Mon, 15 Oct 2018 11:27:58 +0200 Subject: [PATCH 4/4] docs(tree-checklist-example): Remove selection login out of getter Removed the parent selection logic from the getter to its own function. --- .../tree-checklist-example.html | 2 +- .../tree-checklist/tree-checklist-example.ts | 48 ++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/material-examples/tree-checklist/tree-checklist-example.html b/src/material-examples/tree-checklist/tree-checklist-example.html index a92475a65a59..5fc96882e1fa 100644 --- a/src/material-examples/tree-checklist/tree-checklist-example.html +++ b/src/material-examples/tree-checklist/tree-checklist-example.html @@ -3,7 +3,7 @@ {{node.item}} + (change)="todoLeafItemSelectionToggle(node)">{{node.item}} diff --git a/src/material-examples/tree-checklist/tree-checklist-example.ts b/src/material-examples/tree-checklist/tree-checklist-example.ts index dc2ddcd93369..b3c1461cc4b6 100644 --- a/src/material-examples/tree-checklist/tree-checklist-example.ts +++ b/src/material-examples/tree-checklist/tree-checklist-example.ts @@ -174,7 +174,6 @@ export class TreeChecklistExample { const descAllSelected = descendants.every(child => this.checklistSelection.isSelected(child) ); - this.checkRootNodeSelection(node, descAllSelected); return descAllSelected; } @@ -192,11 +191,36 @@ export class TreeChecklistExample { this.checklistSelection.isSelected(node) ? this.checklistSelection.select(...descendants) : this.checklistSelection.deselect(...descendants); + + // Force update for the parent + descendants.every(child => + 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, descAllSelected: boolean): void { + checkRootNodeSelection(node: TodoItemFlatNode): void { const nodeSelected = this.checklistSelection.isSelected(node); + const descendants = this.treeControl.getDescendants(node); + const descAllSelected = descendants.every(child => + this.checklistSelection.isSelected(child) + ); if (nodeSelected && !descAllSelected) { this.checklistSelection.deselect(node); } else if (!nodeSelected && descAllSelected) { @@ -204,6 +228,26 @@ export class TreeChecklistExample { } } + /* Get the parent node of a node */ + getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null { + const currentLevel = this.getLevel(node); + + if (currentLevel < 1) { + return null; + } + + const startIndex = this.treeControl.dataNodes.indexOf(node) - 1; + + for (let i = startIndex; i >= 0; i--) { + const currentNode = this.treeControl.dataNodes[i]; + + if (this.getLevel(currentNode) < currentLevel) { + return currentNode; + } + } + return null; + } + /** Select the category so we can insert the new item. */ addNewItem(node: TodoItemFlatNode) { const parentNode = this.flatNodeMap.get(node);