Expanding to Collapse All Other Nodes in Angular 12
Angular's hierarchical structures, often represented by trees or nested lists, offer a dynamic way to organize data. A common interaction requirement in such structures is the ability to expand a node while collapsing all others. This behavior is essential for creating user interfaces where users focus on a single branch of information at a time. In this article, we'll explore how to achieve this functionality in Angular 12 using a combination of template directives and component logic.
Understanding the Challenge
The core challenge lies in coordinating the expansion and collapse states across multiple nodes within a tree structure. Each node should respond independently to user interaction, ensuring that only the selected node remains expanded while all other nodes are collapsed.
The Solution: A Combined Approach
We'll use a combination of Angular's powerful features to implement this:
-
Template Directives: Angular's
@Directive
decorator provides a way to create custom directives that modify the behavior of HTML elements. We'll create a directive to handle the expansion and collapse logic of individual nodes. -
Component Logic: Our component will manage the global state of all nodes, ensuring that only one node is expanded at a time.
Step-by-Step Implementation
1. Creating the Node Directive:
import { Directive, HostBinding, Input, HostListener } from '@angular/core';
@Directive({
selector: '[appNode]'
})
export class NodeDirective {
@Input() expanded = false;
@HostBinding('class.expanded')
get isExpanded(): boolean {
return this.expanded;
}
@HostListener('click')
onClick() {
// Emit event to notify the component about the node's click
}
}
Explanation:
- The
appNode
selector indicates that this directive will be applied to elements with the attributeappNode
. @Input()
allows the directive to receive anexpanded
property from the parent component.@HostBinding()
binds theexpanded
property to theexpanded
CSS class on the element. This class will be applied when the node is expanded.@HostListener()
listens for click events on the element. When clicked, theonClick()
method will be called.
2. Implementing the Component Logic:
import { Component } from '@angular/core';
@Component({
selector: 'app-tree-view',
templateUrl: './tree-view.component.html',
styleUrls: ['./tree-view.component.css']
})
export class TreeViewComponent {
nodes = [
{ id: 1, name: 'Node 1', expanded: false, children: [
{ id: 11, name: 'Node 1-1', expanded: false, children: [] }
] },
{ id: 2, name: 'Node 2', expanded: false, children: [
{ id: 21, name: 'Node 2-1', expanded: false, children: [] },
{ id: 22, name: 'Node 2-2', expanded: false, children: [] }
] },
];
// Method to handle node click
onNodeClick(node: any) {
// Toggle the node's expansion state
node.expanded = !node.expanded;
// Collapse all other nodes
this.nodes.forEach(n => {
if (n.id !== node.id) {
n.expanded = false;
}
});
}
}
Explanation:
- The
nodes
array represents the tree data structure. - The
onNodeClick()
method handles clicks on individual nodes. - When a node is clicked:
- The node's expansion state is toggled.
- All other nodes are collapsed.
3. Using the Directive and Component:
-
{{ node.name }}
-
{{ child.name }}
Explanation:
- The
appNode
directive is applied to the<li>
elements representing nodes. - The
[expanded]
binding passes the node'sexpanded
state to the directive. - The
(click)
event emits a click event to the component'sonNodeClick()
method.
Example Usage
Let's consider a tree structure for displaying a file system:
-
{{ node.name }}
-
{{ child.name }}
-
{{ grandchild.name }}
In this example, clicking on a folder node will expand that folder and collapse all other folders. This behavior is essential for providing a focused user experience when navigating a file system.
Conclusion
By utilizing template directives and component logic, we've effectively implemented a solution for expanding a node while collapsing all others in Angular 12. This approach combines code reusability with component-level control over the global state of the tree structure, leading to a more streamlined and maintainable codebase.
This solution provides a foundation for implementing more complex interactions with hierarchical data structures in Angular applications. By extending this approach, you can introduce features like lazy loading of subtrees, dynamic node creation, and custom animations to enhance the user experience.