Angular 15-18 Quick Reference
Modern Features Matrix
| Feature | Version | Status | Use When |
|---|---|---|---|
| Standalone Components | 15 | Stable | All new projects |
| Signals | 16-17 | Stable | Local reactive state |
| New Control Flow | 17 | Stable | All templates |
| Signal Inputs | 17.1 | Stable | Component inputs |
| Zoneless CD | 18+ | Experimental | Max performance |
| SSR Hydration | 16+ | Stable | SEO, performance |
Change Detection Strategies
| Strategy | Performance | Use Case | Pros | Cons |
|---|---|---|---|---|
| Default | ★☆☆ | Prototypes | Easy | Slow |
| OnPush | ★★★ | Production | Fast | Manual triggers |
| Signals | ★★★★★ | Modern apps | Fastest | Learning curve |
| Zoneless | ★★★★★ | Performance-critical | Predictable | Requires signals |
State Management Decision Tree
How large is your state?
│
├─ Component-level
│ └─ Use: Signals
│ const count = signal(0);
│
├─ Feature-level
│ └─ Use: Signal Store
│ @Injectable() class FeatureStore { }
│
└─ Application-level
└─ Use: NgRx + Signals
Store with signal selectors
Performance Optimization Checklist
✅ Must Have
- OnPush change detection
- Lazy loading for routes
- TrackBy in
@forloops - NgOptimizedImage for images
- Bundle size < 500KB
✅ Should Have
- Preloading strategy
-
@deferfor heavy components - Code splitting
- Tree-shaking optimization
- CDN for static assets
✅ Nice to Have
- SSR with hydration
- Service Worker (PWA)
- Image CDN integration
- Zoneless mode
- Compression (Brotli)
Common Patterns
1. Signal Component Store
@Injectable()
export class TodoStore {
private todos = signal<Todo[]>([]);
private filter = signal<"all" | "active" | "completed">("all");
readonly todos$ = this.todos.asReadonly();
readonly filteredTodos = computed(() => {
const todos = this.todos();
const filter = this.filter();
if (filter === "all") return todos;
return todos.filter((t) => (filter === "active" ? !t.completed : t.completed));
});
addTodo(text: string) {
this.todos.update((todos) => [...todos, { id: Date.now(), text, completed: false }]);
}
setFilter(filter: "all" | "active" | "completed") {
this.filter.set(filter);
}
}
2. Functional Guard
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isAuthenticated()) {
return true;
}
return router.createUrlTree(["/login"], {
queryParams: { returnUrl: state.url },
});
};
3. Functional Resolver
export const userResolver: ResolveFn<User> = (route, state) => {
const userService = inject(UserService);
const id = route.paramMap.get("id")!;
return userService.getUser(id);
};
4. Custom Pipe
@Pipe({ name: "timeAgo", standalone: true })
export class TimeAgoPipe implements PipeTransform {
transform(value: Date): string {
const seconds = Math.floor((Date.now() - value.getTime()) / 1000);
if (seconds < 60) return "just now";
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
return `${Math.floor(seconds / 86400)}d ago`;
}
}
5. HTTP Interceptor
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const token = authService.getToken();
if (token) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
}
return next(req);
};
// Provide in main.ts
provideHttpClient(withInterceptors([authInterceptor]));
API Quick Reference
Signals
// Create
const count = signal(0);
const user = signal<User | null>(null);
// Read
console.log(count());
// Write
count.set(10);
count.update((n) => n + 1);
// Computed
const doubled = computed(() => count() * 2);
// Effect
effect(() => console.log("Count:", count()));
// Readonly
const readonlyCount = count.asReadonly();
Control Flow
// @if
@if (condition) { <div>True</div> } @else { <div>False</div> }
// @for
@for (item of items; track item.id) { <div>{{ item.name }}</div> }
// @switch
@switch (value) {
@case (1) { <div>One</div> }
@default { <div>Other</div> }
}
// @defer
@defer (on viewport) {
<heavy-component />
} @loading { <spinner /> } @placeholder { <skeleton /> }
Routing
// Route config
export const routes: Routes = [
{
path: "feature",
loadComponent: () => import("./feature.component").then((m) => m.FeatureComponent),
canActivate: [authGuard],
resolve: { data: dataResolver },
},
];
// Bootstrap
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes, withPreloading(PreloadAllModules)), provideHttpClient()],
});
CLI Commands
# Create standalone component
ng generate component my-component --standalone
# Create standalone pipe
ng generate pipe my-pipe --standalone
# Create service
ng generate service my-service
# Migrate to control flow
ng generate @angular/core:control-flow
# Build for production
ng build --configuration production
# Analyze bundle
ng build --stats-json
npx webpack-bundle-analyzer dist/stats.json
Interview Quick Prep
30-Second Explanations
Standalone Components: "Self-contained Angular components that don't need NgModules. They declare their dependencies directly, enabling better tree-shaking and simpler architecture."
Signals: "Fine-grained reactive primitives that automatically track dependencies and update only affected DOM nodes, eliminating Zone.js overhead."
OnPush Strategy: "Change detection strategy that only checks components when inputs change by reference or events fire, dramatically improving performance."
@defer: "Declarative lazy loading in templates with triggers like viewport, interaction, or idle, plus built-in loading/error states."
Zoneless: "Running Angular without Zone.js by using signals for reactivity, providing predictable performance and smaller bundles."
Resources
Official Documentation
- Angular.dev - New official docs
- Angular Blog - Latest updates
- Angular GitHub - Source code
Learning
Tools
- Angular DevTools
- Nx - Monorepo tools
- StackBlitz - Online IDE
Community
Keep this reference handy during development and interviews!