Mistakes I Made as a Beginner Angular Developer

Mistakes I Made as a Beginner Angular Developer

Starting out with Angular can be a challenging yet rewarding journey. As a beginner, I made several mistakes that were valuable learning experiences. In this post, I’ll share some common pitfalls and how to avoid them, along with code examples to illustrate these errors and their solutions.

1. Ignoring Angular CLI

One of my first mistakes was not fully utilizing Angular CLI. The Angular CLI is a powerful tool that streamlines the development process, but I initially tried to create everything manually.

Mistake: Manually setting up a project without using CLI commands can lead to missing important configurations.

# Incorrect way to initialize
mkdir my-angular-app
cd my-angular-app
# Manual setup of tsconfig, angular.json, etc.        

Solution: Use Angular CLI to set up your project correctly.

ng new my-angular-app
cd my-angular-app
ng serve        

2. Overusing any Type

In the early days, I frequently used the any type to bypass TypeScript’s strict type checking. This practice led to runtime errors and hard-to-debug issues.

Mistake:

let user: any = { name: 'John', age: 30 };
console.log(user.address); // Runtime error, address does not exist        

Solution: Define proper interfaces or types to ensure type safety.

interface User {
  name: string;
  age: number;
  address?: string; // Optional property
}

let user: User = { name: 'John', age: 30 };
console.log(user.address); // No error, but address is undefined        

3. Forgetting to Unsubscribe from Observables

I often forgot to unsubscribe from Observables, leading to memory leaks and performance issues.

Mistake:

// Example of subscribing without unsubscribing
ngOnInit() {
  this.dataService.getData().subscribe(data => {
    this.data = data;
  });
}        

Solution: Use the takeUntil operator or unsubscribe in the ngOnDestroy lifecycle hook.

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export class MyComponent implements OnDestroy {
  private destroy$ = new Subject<void>();

  ngOnInit() {
    this.dataService.getData().pipe(takeUntil(this.destroy$)).subscribe(data => {
      this.data = data;
    });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}        

4. Not Leveraging Angular’s Dependency Injection Properly

I initially created services manually and instantiated them directly, rather than using Angular’s dependency injection.

Mistake:

// Directly creating an instance of the service
const userService = new UserService();
userService.getUser();        

Solution: Let Angular manage service instances via dependency injection.

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  getUser() {
    // Implementation
  }
}

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
})
export class UserComponent {
  constructor(private userService: UserService) {
    this.userService.getUser();
  }
}        

5. Not Using Angular’s Built-in Directives

In the beginning, I would manually handle tasks that Angular’s built-in directives could manage more efficiently.

Mistake:

<!-- Manual handling -->
<div *ngIf="showElement">Element is visible</div>

<button (click)="toggleElement()">Toggle Element</button>        

Solution: Use Angular’s structural directives and built-in functionalities.

// Component code
export class MyComponent {
  showElement = false;

  toggleElement() {
    this.showElement = !this.showElement;
  }
}        
<!-- Angular way -->
<div *ngIf="showElement">Element is visible</div>
<button (click)="toggleElement()">Toggle Element</button>        

6. Incorrectly Using Angular Lifecycle Hooks

One of the more subtle mistakes I made was not understanding when Angular lifecycle hooks are called, leading to issues with data binding and component initialization.

Mistake:

export class MyComponent implements OnInit {
  data: any;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    // Incorrect: Attempting to access data before initialization
    this.dataService.getData().subscribe(data => {
      this.data = data;
    });
  }
}        

Solution: Ensure that you’re handling asynchronous data correctly and understand how lifecycle hooks work.

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-my',
  templateUrl: './my.component.html',
})
export class MyComponent implements OnInit {
  data: any;
  isDataLoaded = false;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.dataService.getData().subscribe(data => {
      this.data = data;
      this.isDataLoaded = true; // Set flag once data is loaded
    });
  }
}        

7. Misusing Angular’s Change Detection Strategy

I initially struggled with Angular’s change detection strategy. Not understanding the difference between Default and OnPush strategies led to performance issues.

Mistake:

@Component({
  selector: 'app-my',
  templateUrl: './my.component.html',
  changeDetection: ChangeDetectionStrategy.Default
})
export class MyComponent {
  data = { value: 0 };

  updateData() {
    this.data.value++;
  }
}        

Solution: Switch to ChangeDetectionStrategy.OnPush for better performance and handle immutability correctly.

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
  selector: 'app-my',
  templateUrl: './my.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
  data = { value: 0 };

  updateData() {
    this.data = { ...this.data, value: this.data.value + 1 }; // Immutability
  }
}        

8. Misconfiguring Lazy Loading Routes

I struggled with configuring lazy loading properly, which led to modules not loading as expected and performance issues.

Mistake:

const routes: Routes = [
  { path: 'feature', component: FeatureComponent }
];        

Solution: Configure lazy-loaded modules correctly to optimize loading times.

const routes: Routes = [
  { path: 'feature', loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule) }
];        

In the FeatureModule:

@NgModule({
  declarations: [FeatureComponent],
  imports: [CommonModule, RouterModule.forChild([{ path: '', component: FeatureComponent }])]
})
export class FeatureModule {}        

9. Incorrect Handling of Form Validation

Initially, I mishandled reactive forms and validation, leading to issues where validation was not being applied correctly.

Mistake:

import { FormBuilder, FormGroup } from '@angular/forms';

export class MyComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      name: ''
    });
  }
}        

Solution: Implement proper validation and error handling with reactive forms.

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class MyComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      name: ['', Validators.required]
    });
  }

  onSubmit() {
    if (this.form.valid) {
      console.log(this.form.value);
    } else {
      console.log('Form is invalid');
    }
  }
}        

10. Incorrectly Handling HTTP Requests

I initially had issues with error handling and response processing for HTTP requests.

Mistake:

export class MyComponent {
  constructor(private http: HttpClient) {}

  fetchData() {
    this.http.get('api/data').subscribe(
      data => console.log(data),
      error => console.error('Error fetching data', error) // Basic error handling
    );
  }
}        

Solution: Implement more comprehensive error handling and response processing.

import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, Observable, throwError } from 'rxjs';

export class MyComponent {
  constructor(private http: HttpClient) {}

  fetchData(): Observable<any> {
    return this.http.get('api/data').pipe(
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    console.error('Error fetching data', error);
    return throwError('Something went wrong; please try again later.');
  }
}        

Conclusion

These advanced mistakes and solutions highlight the importance of understanding Angular’s deeper concepts and functionalities. By avoiding these pitfalls and following best practices, you’ll build more efficient and maintainable Angular applications. If you have any more complex scenarios or tips from your own experience, feel free to share them in the comments!

To view or add a comment, sign in

More articles by Sehban Alam

Others also viewed

Explore content categories