LGT

Building Production-Ready Angular Applications

Building Production-Ready Angular Applications

Building Production-Ready Angular Applications

Creating Angular applications that are ready for production requires careful attention to performance, security, and maintainability. In this guide, we'll cover the essential practices and tools you need to build robust applications.

Performance Optimization

1. Lazy Loading

Implement lazy loading for feature modules to reduce initial bundle size:

const routes: Routes = [
  {
    path: 'dashboard',
    loadComponent: () =>
      import('./dashboard/dashboard.component').then((m) => m.DashboardComponent),
  },
  {
    path: 'profile',
    loadComponent: () => import('./profile/profile.component').then((m) => m.ProfileComponent),
  },
];

2. OnPush Change Detection

Use OnPush change detection strategy for better performance:

@Component({
  selector: 'app-user-card',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div>
      <h3>{{ user.name }}</h3>
      <p>{{ user.email }}</p>
    </div>
  `,
})
export class UserCardComponent {
  @Input() user!: User;
}

3. TrackBy Functions

Implement trackBy functions for *ngFor loops to minimize DOM manipulation:

@Component({
  template: `
    @for (item of items; track item.id) {
      <div>{{ item.name }}</div>
    }
  `,
})
export class ItemListComponent {
  items: Item[] = [];
}

Security Best Practices

1. Sanitize User Input

Always sanitize user input to prevent XSS attacks:

import { DomSanitizer } from '@angular/platform-browser';

@Component({
  template: `<div [innerHTML]="sanitizedContent"></div>`,
})
export class SafeContentComponent {
  constructor(private sanitizer: DomSanitizer) {}

  sanitizedContent = this.sanitizer.bypassSecurityTrustHtml(userContent);
}

2. Implement Authentication Guards

Protect routes with authentication guards:

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private authService: AuthService,
    private router: Router,
  ) {}

  canActivate(): boolean {
    if (this.authService.isAuthenticated()) {
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}

Error Handling

1. Global Error Handler

Implement a global error handler for better error management:

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: any): void {
    console.error('Global error:', error);
    // Send to error reporting service
  }
}

2. HTTP Error Interceptor

Handle HTTP errors consistently:

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          // Handle unauthorized
        } else if (error.status === 500) {
          // Handle server error
        }
        return throwError(() => error);
      }),
    );
  }
}

Testing Strategy

1. Unit Testing

Write comprehensive unit tests for your components and services:

describe('UserService', () => {
  let service: UserService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UserService],
    });
    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  it('should fetch users', () => {
    const mockUsers = [{ id: 1, name: 'John' }];

    service.getUsers().subscribe((users) => {
      expect(users).toEqual(mockUsers);
    });

    const req = httpMock.expectOne('/api/users');
    req.flush(mockUsers);
  });
});

2. E2E Testing

Implement end-to-end tests for critical user flows:

describe('User Authentication', () => {
  it('should allow user to login', () => {
    cy.visit('/login');
    cy.get('[data-cy=email]').type('user@example.com');
    cy.get('[data-cy=password]').type('password');
    cy.get('[data-cy=login-button]').click();
    cy.url().should('include', '/dashboard');
  });
});

Deployment Considerations

1. Environment Configuration

Use environment files for different deployment stages:

export const environment = {
  production: true,
  apiUrl: 'https://api.production.com',
  enableLogging: false,
};

2. Build Optimization

Optimize your build for production:

{
  "build": {
    "configurations": {
      "production": {
        "optimization": true,
        "outputHashing": "all",
        "sourceMap": false,
        "extractLicenses": true,
        "budgets": [
          {
            "type": "initial",
            "maximumWarning": "500kb",
            "maximumError": "1mb"
          }
        ]
      }
    }
  }
}

Monitoring and Analytics

1. Error Tracking

Implement error tracking with services like Sentry:

import * as Sentry from '@sentry/angular-ivy';

Sentry.init({
  dsn: 'YOUR_DSN',
  environment: environment.production ? 'production' : 'development',
});

2. Performance Monitoring

Monitor application performance with tools like Google Analytics or New Relic.

Conclusion

Building production-ready Angular applications requires attention to detail and adherence to best practices. By following these guidelines, you can create robust, performant, and secure applications that provide excellent user experiences.

Remember to continuously monitor your application's performance and user feedback to identify areas for improvement.

Tags

angular production performance best-practices