Angular Rules

Documentation for Developers

Angular 20 Development Rules

Quick reference guide for Angular 20 development standards and modern patterns.

🚀 Modern Initialization Patterns

App Initialization

✅ DO:

// Use provideAppInitializer for async bootstrap operations
provideAppInitializer(() => {
  const service = inject(MyService);
  return service.initialize(); // Returns Promise<void>
})

❌ DON'T:

// Deprecated APP_INITIALIZER token
{ provide: APP_INITIALIZER, useFactory: ..., multi: true }

✅ DO:

// Modern Angular 19+ approach
provideAppInitializer(() => {
  // Initialization logic here
  return async () => {
    // Async initialization
  };
})

Environment Initialization

✅ DO:

// Use provideEnvironmentInitializer for sync environment setup
provideEnvironmentInitializer(() => {
  console.log('Environment ready');
  // Sync setup only
})

❌ DON'T:

// Deprecated ENVIRONMENT_INITIALIZER token
{ provide: ENVIRONMENT_INITIALIZER, useValue: ..., multi: true }

✅ DO:

// Modern Angular 19+ approach for environment setup
provideEnvironmentInitializer(() => {
  // Synchronous environment setup
  if (isDevMode()) {
    console.log('🚀 Application environment initialized');
  }
})

📊 Signal-Based Development

Component State

✅ DO:

// Use signals for reactive state
readonly count = signal(0);
readonly doubleCount = computed(() => this.count() * 2);

// Use InputSignal for inputs
title: InputSignal<string> = input('Default');

❌ DON'T:

// Legacy @Input and manual change detection
@Input() title = 'Default';
constructor(private cdr: ChangeDetectorRef) {}

Effects

✅ DO:

// Modern effect usage (allowSignalWrites deprecated)
effect(() => {
  mySignal.set(computedValue());
});

❌ DON'T:

// Deprecated allowSignalWrites flag
effect(() => {
  mySignal.set(value);
}, { allowSignalWrites: true });

🎯 Template Best Practices

Control Flow

✅ DO:

<!-- Modern control flow -->
@if (user()) {
  @for (item of items(); track item.id) {
    <div>{{ item.name }}</div>
  }
}

@switch (status()) {
  @case ('loading') { <spinner></spinner> }
  @default { <content></content> }
}

❌ DON'T:

<!-- Legacy structural directives -->
<div *ngIf="user">
  <div *ngFor="let item of items; trackBy: trackByFn">
    {{ item.name }}
  </div>
</div>

Template Logic

✅ DO:

// Move logic to computed signals
readonly shouldShow = computed(() => this.mode() === 'active');
@if (shouldShow()) {
  <content></content>
}

❌ DON'T:

<!-- No complex logic in templates -->
<div *ngIf="mode === 'active' && user?.hasPermission">

🏗️ Component Architecture

File Structure

✅ DO:

❌ DON'T:

Dependency Injection

✅ DO:

// Use inject function
export class MyComponent {
  private service = inject(MyService);
}

// For services
@Injectable({ providedIn: 'root' })
export class MyService {}

❌ DON'T:

// Constructor injection in components (prefer inject)
constructor(private service: MyService) {}

🎨 Styling Rules

SCSS Structure

✅ DO:

.parent-class {
  .child-class {
    .grandchild-class {
      // Nested styling
    }
  }
}

Theme Integration

✅ DO:

// Use theme system variables
.component {
  background: var(--primary-color);
  color: var(--text-color);
}

❌ DON'T:

// Hard-coded colors
.component {
  background: #007bff;
  color: #333333;
}

🚫 Prohibited Practices

❌ setTimeout() is STRICTLY PROHIBITED

Why setTimeout() is banned:

❌ NEVER DO:

// BAD: setTimeout() for change detection
ngOnInit() {
  setTimeout(() => {
    this.cdr.markForCheck();
  }, 100);
}

// BAD: setTimeout() for state updates
updateState() {
  setTimeout(() => {
    this.state = newValue;
  }, 0);
}

✅ ALWAYS DO:

// GOOD: Use signals for reactive state
state = signal(initialValue);

// GOOD: Use computed for derived state
derivedState = computed(() => this.state() * 2);

// GOOD: Use effects for side effects
effect(() => {
  console.log('State changed:', this.state());
});

// GOOD: Fix root causes, don't use workarounds
ngOnInit() {
  this.initializeComponent(); // No setTimeout needed
}

When you think you need setTimeout():

  1. State Updates → Use signals and computed values
  2. Change Detection → Fix the root cause properly
  3. Async Operations → Use async/await patterns
  4. UI Updates → Use Angular's reactive system
  5. Timing Issues → Use Angular's built-in mechanisms

📦 Import Organization

// 1. Angular core/common
import { Component, signal, computed } from '@angular/core';

// 2. RxJS
import { Observable, map } from 'rxjs';

// 3. Core libraries
import { CoreService } from '@widgets-editor/core';

// 4. Shared libraries
import { SharedComponent } from '@widgets-editor/shared-ui';

// 5. Feature imports
import { FeatureService } from '../services';

// 6. Relative imports
import './component.scss';

⚡ Performance Guidelines

🛡️ Type Safety

// Use strict typing
interface UserData {
  id: string;
  name: string;
  email?: string;
}

// Generic constraints
function processData<T extends UserData>(data: T): T {
  return data;
}

Remember: These rules ensure consistent, modern Angular development across our monorepo. Always use Angular 20+ patterns and avoid deprecated APIs.