Zoneless in Practice: How to Take Control of Your Angular Application

In this series, you will learn:

  1. Why Start with Zoneless Programming?
  2. How to Remove Dependencies on Zone.js and Introduce Signals in Your Angular Project
  3. Advanced Zoneless Angular Principles: Signals, Effects, and New Opportunities
  4. How to Properly Optimize a Zoneless Project and Monitor Its Performance
  5. Tips, Tricks, and the Future of Zoneless Angular: Real-World Examples

In the previous blog, we explored why it’s worth diving into zoneless programming in Angular and the benefits of removing the Zone.js dependency. Now it’s time to move from theory to practice. We’ll guide you through the concrete steps to successfully migrate your project to a zoneless architecture. We’ll focus on practical tools to ease the transition and demonstrate how to implement manual change detection effectively using signals.

Preparing for the Transition

New Projects

If you’re starting a new project, you have the advantage of implementing a zoneless architecture from the ground up. Simply call provideExperimentalZonelessChangeDetection() in the application root:

Note: The provideExperimentalZonelessChangeDetection feature is currently marked as experimental (flagged as native experimental) and may be modified or removed after stabilization.

Next, remove Zone.js from polyfills.ts, angular.json, and package.json. This creates a clean environment free of unnecessary dependencies, leading to smaller application size, faster load times, and overall better performance.

Existing Projects

If you already have a working application, it’s not advisable to remove Zone.js immediately. It’s better to take a gradual approach, starting by setting the following for each component:

changeDetection: ChangeDetectionStrategy.OnPush

  1. This strategy minimizes component rendering triggered by global changes.
  2. Use: 
    • markForCheck(): To schedule a component re-render with OnPush. This performance-friendly approach allows for efficient change management.
    • detectChanges(): To trigger immediate change detection and rendering during a method or function. Use cautiously, as it can negatively impact application performance.

This change management process requires manual intervention. Fortunately, signals effectively replace this need. Signals “listen” to emitted values and allow Angular to automatically update templates based on value changes.

OnPush ensures that rendering responds only to signal changes or, in rare cases, manual markForCheck calls, avoiding unnecessary interference from other processes.

Signals: Smart Change Management

What Are Signals?

Signals introduce an automated dependency system between data and their tracking. They create a dependency tree efficiently managed by Angular. Signals can be:

  • Read-only: For value tracking.
  • Writable: For value updates.

Creating a Writable Signal

WritableSignal also offers a useful method, asReadonly(), to convert a Writable signal into a regular read-only signal. This ensures the signal serves as a value producer without direct modifications outside its primary context.

Using computed: Automating Derived Values

Computed signals are a special type of read-only signals that dynamically calculate their value based on dependencies defined in their logic. Their key advantage is automatic dependency tracking and value updates, eliminating the need for manual updates or dependency management.

Usage Example

In this example:

  1. showCount determines whether the count value should be displayed.
  2. conditionalCount is a computed signal that reacts only to changes in the showCount or count signals, but only when necessary.

Computed signals have specific properties:

  1. Dynamic dependencies: When conditionalCount is first used, its function body runs, and dependencies are automatically “calculated.” For example, conditionalCount depends on showCount. If count changes, computed won’t trigger unless count was activated as a dependency.
  2. Lazy evaluation: The computed function executes only when its result is actually used. If conditionalCount is never called, its derivation function doesn’t run.
  3. Dynamic dependency reconfiguration: If showCount changes to true, computed recalculates its function and adds count as a dependency as needed. This process is automatic and updates dependencies dynamically.

Hidden Tricks of computed

  1. Dependencies on other computed signals: Computed signals can depend on other computed signals. Since every signal produces a value, computed signals can be chained, enabling complex derivations without manual dependency tracking.
  2. Immutable from the outside: computed signals cannot be directly modified. They are designed for value derivation, preventing unintended changes. However, Angular plans to introduce mixed producers like linkedSignal, introduced in Angular 19, in the future.

Using Effects: Automating Specific Scenarios

Angular also introduces the concept of effects (effect) within signals, enabling tracking and executing actions based on changes. Effects are particularly useful for scenarios where you need to react to signal value changes without affecting application state:

Example of Using an Effect:

Effects are helpful for various tasks such as:

  • Writing to the DOM or localStorage.
  • Triggering side effects without modifying state.
  • Automatically tracking changes without manual checks.

Effects complement signals, computed logic, and manual change management, creating a comprehensive and flexible architecture for modern Angular applications.

Migrating to a zoneless architecture is more than just removing Zone.js. It’s a new way of thinking, giving you control over when and where rendering occurs within the application. 

Signals, together with computed logic and effects, create a robust architecture that minimizes the need for manual change management, enabling faster and more efficient development.

In the next part, we’ll dive into advanced concepts, such as transformations between signals and observables, handling destroyed components and contexts, and leveraging signals for even more efficient architecture.

If you’re already intrigued by what signals can offer, know that this is just the beginning.

Interested in development? You might enjoy more articles on this topic!