In this article, we’ll explore the top 10 Angular mistakes and how to avoid them with best practices and code examples.
1️⃣ Not Using OnPush
Change Detection Strategy
❌ Mistake: Using Default Change Detection for All Components
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
})
export class ExampleComponent {
@Input() data: any;
}
✔ Issue:
- Angular checks all components in the component tree, causing unnecessary performance issues.
✅ Solution: Use OnPush
for Better Performance
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent {
@Input() data: any;
}
✔ Benefit: Reduces unnecessary re-rendering, improving performance.
2️⃣ Not Using TrackBy in *ngFor
Loops
❌ Mistake: Using *ngFor
Without trackBy
<li *ngFor="let item of items">{{ item.name }}</li>
✔ Issue:
- Angular re-renders the entire list when data changes, reducing performance.
✅ Solution: Use trackBy
to Improve Rendering Performance
<li *ngFor="let item of items; trackBy: trackByFn">{{ item.name }}</li>
trackByFn(index: number, item: any): number {
return item.id; // ✅ Unique identifier
}
✔ Benefit: Only updates changed items, improving UI performance.
3️⃣ Subscribing to Observables Without Unsubscribing
❌ Mistake: Not Unsubscribing from Observables
ngOnInit() {
this.dataService.getData().subscribe(data => {
this.items = data;
});
}
✔ Issue:
- Memory leaks occur if the subscription is not properly unsubscribed.
✅ Solution: Use takeUntil
and Subject
to Unsubscribe
private unsubscribe$ = new Subject<void>();
ngOnInit() {
this.dataService.getData()
.pipe(takeUntil(this.unsubscribe$))
.subscribe(data => this.items = data);
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete(); // ✅ Prevents memory leaks
}
✔ Benefit: Ensures subscriptions are properly cleaned up.
4️⃣ Not Lazy Loading Modules
❌ Mistake: Loading All Modules Eagerly
import { UserModule } from './user/user.module';
@NgModule({
imports: [UserModule], // ❌ Loads everything on startup
})
export class AppModule {}
✔ Issue:
- Increases initial load time, affecting performance.
✅ Solution: Use Lazy Loading with Routes
const routes: Routes = [
{ path: 'user', loadChildren: () => import('./user/user.module').then(m => m.UserModule) }
];
✔ Benefit: Loads only when needed, reducing initial load time.
5️⃣ Using Direct DOM Manipulation
❌ Mistake: Using document.querySelector()
Instead of Angular Renderer
document.querySelector('#myDiv').style.backgroundColor = 'red'; // ❌ Bad practice
✔ Issue:
- Bypasses Angular's rendering process, leading to unexpected UI behavior.
✅ Solution: Use Renderer2
for DOM Manipulation
constructor(private renderer: Renderer2, private el: ElementRef) {}
changeColor() {
this.renderer.setStyle(this.el.nativeElement, 'background-color', 'red');
}
✔ Benefit: Maintains Angular’s rendering integrity.
6️⃣ Using any
Instead of Proper TypeScript Types
❌ Mistake: Not Defining Proper Types
let user: any; // ❌ Not type-safe
✔ Issue:
- No type checking, leading to runtime errors.
✅ Solution: Use TypeScript’s Strong Typing
interface User {
id: number;
name: string;
}
let user: User = { id: 1, name: 'John' }; // ✅ Type-safe
✔ Benefit: Prevents type-related errors during development.
7️⃣ Calling HTTP Requests Inside Components
❌ Mistake: Fetching Data Directly in a Component
export class ExampleComponent {
constructor(private http: HttpClient) {}
fetchData() {
this.http.get('https://api.example.com/data')
.subscribe(data => console.log(data));
}
}
✔ Issue:
- Violates separation of concerns.
✅ Solution: Use a Service for API Calls
@Injectable({ providedIn: 'root' })
export class DataService {
constructor(private http: HttpClient) {}
fetchData(): Observable<any> {
return this.http.get('https://api.example.com/data');
}
}
✔ Benefit: Keeps components clean and maintainable.
8️⃣ Using console.log()
Instead of Proper Logging
❌ Mistake: Using console.log()
for Debugging
console.log('Error:', error);
✔ Issue:
- Cannot be disabled easily in production.
✅ Solution: Use Angular’s Logging Service (NGXLogger
)
import { NGXLogger } from 'ngx-logger';
constructor(private logger: NGXLogger) {}
this.logger.error('An error occurred', error);
✔ Benefit: Structured logging with better control.
9️⃣ Using Too Many ngIf
and ngSwitch
in Templates
❌ Mistake: Overusing *ngIf
for Conditional Rendering
<div *ngIf="role === 'admin'">Admin Panel</div>
<div *ngIf="role === 'user'">User Panel</div>
✔ Issue:
- Performance issues with multiple conditions.
✅ Solution: Use ngSwitch
for Better Performance
<div [ngSwitch]="role">
<div *ngSwitchCase="'admin'">Admin Panel</div>
<div *ngSwitchCase="'user'">User Panel</div>
</div>
✔ Benefit: Optimizes rendering performance.
🔟 Not Securing Angular Applications
❌ Mistake: Not Using Route Guards for Authentication
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent } // ❌ No authentication check
];
✔ Issue:
- Unauthenticated users can access private routes.
✅ Solution: Use Route Guards for Security
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.authService.isAuthenticated()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
✔ Benefit: Protects sensitive routes from unauthorized users.
🎯 Conclusion
Angular is a powerful but complex framework, and avoiding these common mistakes can help you build scalable, maintainable, and high-performance applications.
✔ Use OnPush
change detection for better performance
✔ Optimize lists using trackBy
✔ Unsubscribe from Observables to prevent memory leaks
✔ Use lazy loading to improve app performance
✔ Follow TypeScript best practices
✔ Secure your application with route guards
By following these best practices, you’ll write cleaner, more efficient Angular code! 🚀
Comments
Post a Comment
Leave Comment