Angular is an open-source web framework maintained by Google for building single-page applications (SPAs) and server-rendered web applications.
- Component-Based Architecture: Build reusable UI components.
- Dependency Injection: Manage services and application dependencies.
- Signals: Manage reactive state and track changes.
- Routing: Configure navigation between application views.
- Forms: Handle form data, validation, and user input.
- HTTP Client: Communicate with backend APIs.
- Server-Side Rendering: Generate HTML on the server and hydrate it in the browser.
Features of Angular 22
1. Signal Forms
Signal Forms introduce a signal-based approach to form management, allowing form values, validation, and field state to integrate with Angular's reactive programming model.
- Manage form data using Signals.
- Define validation rules for individual fields.
- Track field states and validation errors reactively.
- Support synchronous and asynchronous validation.
- Connect form controls to the underlying model.
import { Component, signal } from '@angular/core';
import {
form,
FormField,
required,
email,
} from '@angular/forms/signals';
@Component({
selector: 'app-login',
imports: [FormField],
template: `
<form>
<label for="email">Email</label>
<input
id="email"
type="email"
[formField]="loginForm.email"
/>
@if (loginForm.email().invalid() &&
loginForm.email().touched()) {
<p>Please enter a valid email address.</p>
}
</form>
`,
})
export class LoginComponent {
loginModel = signal({ email: '' });
loginForm = form(this.loginModel, (path) => {
required(path.email);
email(path.email);
});
}
2. Asynchronous Signals and Resource APIs
Angular provides Resource APIs for managing asynchronous operations through reactive state. They are useful when loading data from APIs or responding to changes in request parameters.
- Track loading, success, and error states.
- Reload data when reactive request parameters change.
- Integrate asynchronous results with Signals.
- Cancel obsolete requests when supported by the loader.
- Use rxResource() for Observable-based data sources.
import { Component, resource, signal } from '@angular/core';
@Component({
selector: 'app-user',
template: `
<button (click)="loadUser(1)">Load User 1</button>
<button (click)="loadUser(2)">Load User 2</button>
@if (userResource.isLoading()) {
<p>Loading user...</p>
} @else if (userResource.error()) {
<p>Unable to load user.</p>
} @else if (userResource.hasValue()) {
<h2>{{ userResource.value().name }}</h2>
}
`,
})
export class UserComponent {
userId = signal(1);
userResource = resource({
params: () => ({ id: this.userId() }),
loader: async ({ params, abortSignal }) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${params.id}`,
{ signal: abortSignal },
);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
return response.json() as Promise<{ name: string }>;
},
});
loadUser(id: number) {
this.userId.set(id);
}
}
The resource reacts to changes in userId and loads the corresponding user data.
3. Angular Aria
Angular Aria provides accessible, headless directives for building custom UI components. It focuses on interaction behavior and accessibility rather than prescribing a visual design.
Key benefits include:
- Keyboard navigation support.
- Appropriate ARIA attributes.
- Focus management.
- Accessible interaction patterns for custom components.
- Flexibility to apply your own CSS and design system.
Install the package:
npm install @angular/ariaAngular Aria is useful when building reusable components such as menus, tabs, toolbars, and other interactive controls.
4. Built-in Template Control Flow
Angular's built-in control-flow syntax simplifies conditional rendering and list iteration. These features were introduced before Angular 22 and remain important in modern Angular applications.
Using @if:
@if (isLoggedIn) {
<p>Welcome back!</p>
} @else {
<p>Please log in.</p>
}
Using @for:
@for (user of users; track user.id) {
<p>{{ user.name }}</p>
} @empty {
<p>No users found.</p>
}
The track expression helps Angular associate items with their rendered views when a collection changes.
5. Signals for Reactive State Management
Signals provide a reactive approach to managing application state. They allow Angular to track dependencies and update the UI when relevant values change.
Common APIs include:
- signal() for writable state.
- computed() for derived values.
- linkedSignal() for writable state linked to other reactive values.
- effect() for appropriate side effects involving reactive state.
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<p>Count: {{ count() }}</p>
<p>Double: {{ doubleCount() }}</p>
<button (click)="increment()">Increment</button>
`,
})
export class CounterComponent {
count = signal(0);
doubleCount = computed(() => this.count() * 2);
increment() {
this.count.update(value => value + 1);
}
}
When the count changes, Angular updates the displayed count and its computed value. Signals complement RxJS, which remains useful for asynchronous streams and event processing.