Keywords: Angular | *ngIf | [hidden] | this binding | element show hide
Abstract: This article provides a comprehensive examination of two primary methods for showing and hiding elements in Angular: the *ngIf and [hidden] directives. Through analysis of a practical case involving setTimeout function's this binding problem, it explains the working principles, applicable scenarios, and performance impacts of both directives. The article combines DOM manipulation, component lifecycle, and event handling mechanisms to offer complete solutions and best practice recommendations, helping developers better understand Angular's reactive data binding mechanism.
Problem Background and Phenomenon Analysis
In Angular application development, dynamic showing and hiding of elements is a common interaction requirement. Developers typically use *ngIf or [hidden] directives to achieve this functionality, but various unexpected issues may arise in practical applications.
Consider this typical scenario: after a user performs a save operation, a success message needs to be displayed and automatically hidden after 3 seconds. The initial implementation code is as follows:
<div *ngIf="edited" class="alert alert-success alert-dismissible fade in" role="alert">
<strong>List Saved!</strong> Your changes has been saved.
</div>The corresponding component logic:
export class AppComponent implements OnInit {
public edited = false;
saveTodos(): void {
this.edited = true;
setTimeout(function() {
this.edited = false;
console.log(this.edited);
}, 3000);
}
}Superficially, this code appears logical: set edited to true to show the message, then set to false after 3 seconds to hide it. However, in practice, the message does not hide as expected after 3 seconds, even though the console log shows this.edited indeed becomes false, but the interface doesn't update accordingly.
Root Cause: JavaScript's this Binding Mechanism
The core issue lies in JavaScript's dynamic binding characteristic of the this keyword. In the setTimeout callback function, the reference of this changes and no longer points to the component instance, but rather to the global object (window in browser environments).
When executing this.edited = false, it actually modifies the edited property of the global object, not the component instance's property. Therefore, although the console outputs false, Angular's change detection system doesn't detect the change in the component instance's edited property, preventing interface updates.
Solution: Properly Binding this Context
There are multiple methods to solve this problem, with the most direct being using the Function.prototype.bind() method:
saveTodos(): void {
this.edited = true;
setTimeout(function() {
this.edited = false;
}.bind(this), 3000);
}bind(this) ensures that this in the callback function always points to the component instance. In modern JavaScript development, arrow function syntax is more recommended:
saveTodos(): void {
this.edited = true;
setTimeout(() => {
this.edited = false;
}, 3000);
}Arrow functions automatically bind the this context from their definition scope, avoiding the complexity and potential errors of manual binding.
In-depth Comparison of *ngIf and [hidden]
Working Principle of *ngIf Directive
*ngIf is a structural directive that completely adds or removes elements from the DOM based on expression truthiness. When the expression is false, the corresponding element and its children are completely removed from the DOM tree; when the expression becomes true, they are recreated and inserted into the DOM.
Advantages of this mechanism include:
- Memory efficiency: non-displayed elements are completely removed from DOM
- Avoids unnecessary computation: listeners and computations of hidden elements don't execute
- Semantically appropriate for conditional rendering: elements truly "don't exist" in current state
However, there are also limitations:
- Component state loss: all states are initialized upon recreation
- Performance overhead: frequent DOM operations may impact performance
- Reference loss: DOM element references change
Working Mechanism of [hidden] Attribute
[hidden] is an attribute binding that hides elements through CSS's display: none, while the elements remain in the DOM. When [hidden]="true", the browser doesn't render the element, but the DOM structure remains unchanged.
Advantages of this approach include:
- State preservation: all element states and references are maintained
- Performance optimization: avoids frequent DOM operations
- Seamless switching: smoother show/hide transitions
Potential issues:
- Memory usage: hidden elements still consume memory
- Potential performance issues: large hidden elements may affect page performance
- Listeners still running: event listeners etc. may continue executing
Practical Application Scenario Analysis
Scenarios Suitable for *ngIf
*ngIf is the better choice in the following situations:
- Components involve expensive initialization operations
- Complete component state reset is needed
- Elements are rarely displayed or have low display frequency
- Rendering decisions based on complex conditions
For example, cancel operation in edit forms:
<div *ngIf="isEditing" class="edit-form">
<!-- Edit form content -->
</div>Scenarios Suitable for [hidden]
The following situations are more appropriate for [hidden]:
- Element state and reference preservation is required
- Frequent switching of display states
- Elements contain user input or complex states
- Fast-response interactions needed
For example, message notification components:
<div [hidden]="!showMessage" class="alert">
<!-- Notification message -->
</div>Special Considerations in *ngFor Loops
When controlling individual element display states within *ngFor loops, the best practice is to include the state as a property of the iterated object:
<div *ngFor="let item of items">
<div *ngIf="item.isEdited" class="alert">
<strong>Item Updated!</strong> {{item.name}} has been modified.
</div>
</div>Corresponding component code:
export class AppComponent {
items = [
{ name: 'Item 1', isEdited: false },
{ name: 'Item 2', isEdited: false },
{ name: 'Item 3', isEdited: false }
];
updateItem(index: number): void {
this.items[index].isEdited = true;
setTimeout(() => {
this.items[index].isEdited = false;
}, 3000);
}
}Performance Optimization Recommendations
Based on deep understanding of both directives, the following optimization strategies can be formulated:
- For frequently toggled elements, prioritize
[hidden] - For components with high initialization costs, use
*ngIf - Avoid complex
*ngIfconditions within*ngForin large lists - Use
ChangeDetectionStrategy.OnPushto optimize change detection - Consider using
ngTemplateOutletfor conditional rendering
Conclusion
Showing and hiding elements in Angular involves considerations at multiple levels: JavaScript's this binding mechanism, directive working principles, performance impacts, and specific application scenarios. Proper understanding of these concepts is crucial for developing high-quality Angular applications.
Key takeaways:
- Always be mindful of
thisbinding issues in callback functions - Choose appropriate show/hide strategies based on specific requirements
*ngIfsuits conditional rendering,[hidden]suits state preservation- Combine both approaches in complex scenarios
- Continuously focus on performance optimization and user experience
By deeply understanding these concepts and applying them appropriately, developers can create more stable, efficient, and user-friendly Angular applications.