([]);
ngOnInit() {
this.loadUsers();
}
loadUsers() {
this.userService.getUsers().subscribe({
next: users => this.users.set(users),
error: err => this.logger?.error('Failed to load users', err)
});
}
}
```
## New Control Flow (@if, @for)
```typescript
@Component({
template: `
@if (user(); as currentUser) {
Hello, {{ currentUser.name }}
} @else if (loading()) {
Loading...
} @else {
Please log in
}
@for (item of items(); track item.id) {
{{ item.name }}
} @empty {
No items found
}
@switch (status()) {
@case ('pending') {
Pending...
}
@case ('success') {
Success!
}
@default {
Unknown
}
}
`
})
export class ModernControlFlowComponent {
user = signal(null);
loading = signal(false);
items = signal- ([]);
status = signal<'pending' | 'success' | 'error'>('pending');
}
```
## Performance: OnPush & TrackBy
```typescript
@Component({
selector: 'app-product-list',
standalone: true,
imports: [CommonModule],
template: `
@for (product of products(); track trackByProductId($index, product)) {
}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductListComponent {
products = input.required();
// TrackBy for optimal rendering
trackByProductId(index: number, product: Product): number {
return product.id;
}
}
```
## Quick Reference
| Pattern | Angular 17+ Approach |
|---------|---------------------|
| Component | Standalone by default |
| State | Signals (`signal()`, `computed()`) |
| Input | `input()`, `input.required()` |
| Output | `output()` |
| Two-way | `model()` |
| DI | `inject()` function |
| Control Flow | `@if`, `@for`, `@switch` |
| Change Detection | `ChangeDetectionStrategy.OnPush` |