Angular 18: Say Goodbye to NgModule, Accelerate Apps with Signals and Zoneless

Development tutorial - IT technology blog
Development tutorial - IT technology blog

Why is Angular 18 a Performance Revolution?

If you’ve ever “struggled” with older versions of Angular, the app.module.ts file was likely a nightmare. It was the central hub for everything from components to services, causing the file to bloat and become hard to maintain as projects grew, a challenge often solved by using Nx to manage a full-stack TypeScript monorepo. Not to mention, the Zone.js-based Change Detection often wasted resources by forcing the framework to re-scan the entire component tree for even the smallest events.

Angular 18 was born to end those frustrations. With Standalone Components, we officially phase out the cumbersome NgModule. Meanwhile, Signals acts as a powerful “assistant” for more transparent state management. In a recent project with five developers, applying this duo increased productivity by about 30%. Notably, the classic ExpressionChangedAfterItHasBeenCheckedError almost completely disappeared.

Core Concepts: Standalone and Signals

1. Standalone Components: A Lightweight Structure

Previously, a component had to be “registered” within a module to run. Now, each component is an independent entity that manages its own necessary dependencies (imports). This approach makes sharing components or implementing Lazy Loading incredibly simple, which is a major advantage when building high-quality data tables. Your code will be cleaner without redundant intermediary module files.

2. Signals: Lightning-Fast Reactivity

Imagine a Signal like a cell in Excel. When the value of cell A changes, cell B updates automatically without manual intervention. Signals allow Angular to know exactly which component needs to re-render, similar to the efficiency of Signals in React. The framework no longer has to guess or scan the entire application as it did before. This is the key to achieving instant application responsiveness.

Hands-on: Building a Standard Angular 18 Todo List

Theory isn’t enough. Let’s build a task management application to see the difference this new architecture makes.

Step 1: Initialize the Project

First, upgrade the Angular CLI to the latest version. Open your terminal and run:

npm install -g @angular/cli@latest
ng new angular-18-signals --standalone --routing --style=scss
cd angular-18-signals

Since version 17, the --standalone flag has been the default. However, I’ve included it here to emphasize that NgModule is a thing of the past.

Step 2: Implementing Logic with Signals

We will create todo-list.component.ts. This is where the power of Signals is most evident in managing the task list:

import { Component, signal, computed, effect } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-todo-list',
  standalone: true,
  imports: [CommonModule, FormsModule],
  templateUrl: './todo-list.component.html',
  styleUrl: './todo-list.component.scss'
})
export class TodoListComponent {
  // Initialize Signal with sample data
  todoList = signal<{ task: string, completed: boolean }[]>([
    { task: 'Learn Angular 18', completed: false },
    { task: 'Optimize app performance', completed: true }
  ]);

  newTodo = signal('');

  // Computed Signal: Automatically recalculates when todoList changes
  completedCount = computed(() => 
    this.todoList().filter(t => t.completed).length
  );

  constructor() {
    // Effect: Automatically runs when any internal Signal changes
    effect(() => {
      console.log(`Current task count: ${this.todoList().length}`);
    });
  }

  addTodo() {
    if (this.newTodo().trim()) {
      this.todoList.update(todos => [
        ...todos, 
        { task: this.newTodo(), completed: false }
      ]);
      this.newTodo.set('');
    }
  }

  toggleTodo(index: number) {
    this.todoList.update(todos => 
      todos.map((todo, i) => 
        i === index ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }
}

Step 3: Optimizing the Template with New Control Flow

In the HTML file, notice how Signals are called using parentheses (). Additionally, the new @for syntax replaces the bulky *ngFor:

<div class="container">
  <h2>My Tasks</h2>
  
  <div class="input-group">
    <input [ngModel]="newTodo()" (ngModelChange)="newTodo.set($event)" placeholder="What needs to be done...">
    <button (click)="addTodo()">Add</button>
  </div>

  <p>Completed: {{ completedCount() }} / {{ todoList().length }}</p>

  <ul>
    @for (item of todoList(); track $index) {
      <li (click)="toggleTodo($index)" [class.done]="item.completed">
        {{ item.task }}
      </li>
    }
  </ul>
</div>

The @for syntax is not only more readable but also optimizes the diffing algorithm, making the rendering of large lists significantly faster, much like the techniques for handling 100,000 rows of data smoothly.

Peak Performance: Zoneless Angular

The most “valuable” feature of Angular 18 is the ability to run without Zone.js. By removing this library, your application’s bundle size is reduced by about 13KB (gzipped). More importantly, the browser is no longer blocked by continuous background check tasks.

To enable it, configure it in app.config.ts:

import { ApplicationConfig, provideExperimentalZonelessChangeDetection } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideExperimentalZonelessChangeDetection()
  ]
};

After this step, you can confidently remove zone.js from your polyfills file. The application will now run extremely smoothly thanks to the direct change notification mechanism from Signals.

Real-World Advice

Many of you might wonder: “Will Signals replace RxJS?”. The answer is no. Signals are excellent for managing UI State. However, for complex asynchronous tasks like Debounce Search or handling data streams, RxJS remains the top choice. Combine both using toSignal() to leverage the strengths of each.

Conclusion

Angular 18 is more than just a routine update. It marks a shift towards a modern, streamlined, and higher-performance programming era. Mastering Standalone Components and Signals, especially when components use container queries to adapt to their environment, will help you build professional, maintainable web applications that deliver the best user experience. Good luck with your upcoming Angular projects!

Share: