Avoiding re-rendering untouched nodes.
Hello We are currently using the angular tree component in our project but I am noticing a probably unwanted behaviour. When a new node is inserted into the tree the entire view gets re-rendered which is really not a good option when you think that you could possibly have thousands of nodes in the tree and if the view for the nodes is complex (not just a string, but rather a full blown component provided thru' the template API)
Reproduce
- Pick a node for which you want to append a new child
- Extract the child nodes for that node
- Add the node into the child array
- Call update to force the tree to take the new child into account
- Notice that the entire view gets disposed and re-created (undesired)
NOTE: Shortly after posting this i started playing around with node.children observable array, adding elements to it seems to allow us to avoid refreshing the tree with update(). But using splice() on the array causes the trackBy Function inside TreeNodeCollectionComponent to fail
[mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: 'Reaction[TreeNodeCollectionComponent.detectChanges()] TypeError: Cannot read property 'id' of undefined at DefaultIterableDiffer.push../node_modules/angular-tree-component/dist/components/tree-node-collection.component.js.TreeNodeCollectionComponent.trackNode [as _trackByFn] (tree-node-collection.component.js:55)
Additional Info
This is the sensitive code for the tree below. You will notice that the tree is not in a traditional form where you have the Children Outside of the Parent. We opted to nest the children inside the root component of the parent using ng-content. But that should not be a problem and should not require the root to be re-rendered (or all of the adjasent children for that matter when a new child is added). To add a new child we are currently simply getting the children of the TreeComponent Node and splicing the new child there, after which calling update() (which re-renders the entire tree, re-initializing the view anew)
const children = treeNode.getField(CHILDREN_FIELD)
children.splice(to.index, 0, newNode);
tree.update();
<div class="node-content-wrapper"
[treeDrag]="node"
[class]="node.getClass()"
[class.tree-node]="true"
[class.tree-node-leaf]="node.isLeaf"
[class.reference-element]="isReference(node)"
[class.tree-node-expanded]="node.isExpanded && node.hasChildren"
[class.tree-node-collapsed]="node.isCollapsed && node.hasChildren">
<tree-node-drop-slot *ngIf="index === 0" [dropIndex]="node.index"
[node]="node.parent"></tree-node-drop-slot>
<tree-node-wrapper [node]="node" [index]="index" [templates]="templates">
<ng-template #treeNodeTemplate let-node let-index="index">
<pap-container [type]="getType(getModel(node))"
[disabled]="isDisabled(node)"
[execute]="onActionExecute.bind(this, node)"
[collapsed]="node.isCollapsed || !node.isExpanded"
[context]="getContext(node)" [model]="getModel(node)">
<tree-node-drop-slot *ngIf="index === 0" [dropIndex]="node.index" [node]="node"></tree-node-drop-slot>
<tree-node-children [node]="node" [templates]="templates"></tree-node-children>
<tree-node-drop-slot [dropIndex]="node.index + 1" [node]="node"></tree-node-drop-slot>
<div *ngIf="node.hasChildren && !node.visibleChildren.length" class="no-filter-results">
<span><mat-icon class="search-icon"></mat-icon>{{ 'search.results-not-found' | translate }}</span>
</div>
</pap-container>
</ng-template>
</tree-node-wrapper>
<tree-node-drop-slot [dropIndex]="node.index + 1" [node]="node.parent"></tree-node-drop-slot>
</div>
#Additional Information We are using the virtual scrolling functionality.