Angular Core Concepts: Standalone Components, Signals, and the New Control Flow
These articles are AI-generated summaries. Please check the original sources for full details.
Core Concepts
Atilla Baspinar published a technical guide covering Angular’s core concepts on June 29, 2026. The article details 19 essential topics from standalone components to signals and the new @for/@if control flow.
Why This Matters
The transition from Zone.js-based change detection with decorators like @Input() and @Output() to signal-based reactivity with input(), output(), and model() represents a fundamental shift in Angular architecture. Developers must understand these changes to avoid runtime bugs such as destroying writable signals when using [(ngModel)] directly, or misusing output() which is not a signal despite its similar API family.
Key Insights
- Standalone components (standalone: true) have been the default since Angular 17, eliminating the need for NgModule declarations.
- Signals introduced as reactive primitives with computed() for derived values, replacing decorator-based change detection for many use cases.
- Signal inputs (input()) replace @Input() decorators but are read-only — calling .set() on them is not allowed.
- output() function (Angular 17.3+) replaces @Output() + EventEmitter but returns OutputEmitterRef
which only has .emit(), not signal read capabilities. - @for control flow requires a track expression (similar to key in React) and supports implicit variables like $index, $first, $last, $even, $odd.
Working Examples
Conditional rendering using built-in @if/@else if/@else control flow
@if (status === 'loading') {
<p>Loading...</p>
} @else if (status === 'error') {
<p>Something went wrong.</p>
} @else {
<p>{{ data }}</p>
}
List rendering with @for including an @empty fallback block
@for (user of users; track user.id) {
<li>{{ user.name }}</li>
} @empty {
<li>No users found.</li>
}
Correct two-way binding with a writable signal using split ngModel syntax
<input [ngModel]="title()" (ngModelChange)="title.set($event)" name="title" />
Singleton service decorated with @Injectable and providedIn root
@Injectable({ providedIn: 'root' })
export class TasksService {
private tasks = ['Task 1', 'Task 2'];
getTasks() {
return this.tasks;
}
}
Practical Applications
- —use-case—: Migrating from decorator-based inputs to signal inputs for better type safety and performance. —pitfall—: Attempting to call .set() on input() will throw a compile-time error because signal inputs are read-only.
- —use-case—: Using model() for custom two-way binding components eliminates the need for separate @Input() + @Output() pairs. —pitfall—: Using [(ngModel)] directly on a writable signal destroys the signal at runtime because it desugars to ngModelChange assigning the raw string.
- —use-case—: Implementing content projection with named slots using select attribute in
. —pitfall—: Forgetting that projects children only at component instantiation; dynamic updates require different patterns.
References:
Continue reading
Next article
Why GLM 5.2's MIT License Doesn't Make It Free: The US$1M Hardware Reality
Related Content
Angular Tutorials: A Structured Reference for Modern Angular Devs
A focused 15-part tutorial series covering Angular 17–22, signals, standalone components, and zoneless change detection.
Master Angular Class and Style Binding: Dynamic CSS Made Simple
Angular enables dynamic CSS class and inline style binding using bracket syntax, supporting single and multiple bindings that merge safely with static classes.
Angular Component Selectors: Enhancing Native Elements Without Extra DOM Nodes
Angular supports attribute and class component selectors for attaching components to native HTML elements without extra DOM nodes.