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:
- Separate files:
.ts,.html,.scss - Standalone components:
standalone: true - OnPush change detection where appropriate
❌ DON'T:
- Inline templates or styles
- NgModules for new components
- Default change detection unnecessarily
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
- Never use setTimeout() → Use reactive patterns
- Never use NgModules → Use standalone components
- **Never use ngIf/ngFor → Use @if/@for
- Never inline templates → Use separate .html files
- Never use 'any' type → Use proper TypeScript typing
- Never bypass change detection → Use signals for reactivity
❌ setTimeout() is STRICTLY PROHIBITED
Why setTimeout() is banned:
- Creates timing dependencies and race conditions
- Bypasses Angular's change detection system
- Makes code harder to test and debug
- Indicates underlying architectural issues
❌ 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():
- State Updates → Use signals and computed values
- Change Detection → Fix the root cause properly
- Async Operations → Use async/await patterns
- UI Updates → Use Angular's reactive system
- 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
- Use
OnPushchange detection strategy - Implement
@deferfor heavy components - Use track functions in
@forloops - Prefer computed signals for derived state
- Minimize template expressions
🛡️ 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.