DEV Community

Alex Spinov
Alex Spinov

Posted on

Angular 19 Has Free Signals That Finally Make Angular Feel Modern

Angular was known for heavy boilerplate. Angular 19 with signals, standalone components, and new control flow is a completely different framework — modern, fast, and surprisingly pleasant.

What Changed

Before (Angular 14) After (Angular 19)
NgModules required Standalone components
RxJS for everything Signals (reactive primitives)
Zone.js (monkey-patching) Zoneless (signal-based change detection)
*ngIf, *ngFor directives @if, @for control flow
Heavy boilerplate Clean, minimal code

Signals (Reactive State)

import { signal, computed, effect } from "@angular/core";

@Component({
  standalone: true,
  template: `
    <h1>Count: {{ count() }}</h1>
    <p>Doubled: {{ doubled() }}</p>
    <button (click)="increment()">+1</button>
  `,
})
export class CounterComponent {
  count = signal(0);
  doubled = computed(() => this.count() * 2);

  constructor() {
    effect(() => {
      console.log(`Count changed to ${this.count()}`);
    });
  }

  increment() {
    this.count.update(c => c + 1);
  }
}
Enter fullscreen mode Exit fullscreen mode

No RxJS. No subscriptions. No memory leaks.

New Control Flow

<!-- Before: structural directives -->
<div *ngIf="user; else loading">
  <div *ngFor="let item of items; trackBy: trackById">
    {{ item.name }}
  </div>
</div>

<!-- After: built-in control flow -->
@if (user) {
  @for (item of items; track item.id) {
    <div>{{ item.name }}</div>
  } @empty {
    <p>No items found</p>
  }
} @else {
  <p>Loading...</p>
}
Enter fullscreen mode Exit fullscreen mode

Standalone Components (No NgModules)

@Component({
  standalone: true,
  imports: [CommonModule, RouterLink],
  template: `
    <nav>
      <a routerLink="/">Home</a>
      <a routerLink="/about">About</a>
    </nav>
  `,
})
export class NavComponent {}
Enter fullscreen mode Exit fullscreen mode

Resource API (Data Fetching)

import { resource } from "@angular/core";

@Component({
  standalone: true,
  template: `
    @if (users.isLoading()) {
      <p>Loading...</p>
    } @else {
      @for (user of users.value(); track user.id) {
        <div>{{ user.name }}</div>
      }
    }
  `,
})
export class UsersComponent {
  users = resource({
    loader: () => fetch("/api/users").then(r => r.json()),
  });
}
Enter fullscreen mode Exit fullscreen mode

Deferrable Views

@defer (on viewport) {
  <heavy-chart-component />
} @placeholder {
  <div>Chart will load when scrolled into view</div>
} @loading (minimum 500ms) {
  <spinner />
}
Enter fullscreen mode Exit fullscreen mode

Lazy-load components based on viewport, interaction, timer, or idle.

SSR (Server-Side Rendering)

ng new my-app --ssr
Enter fullscreen mode Exit fullscreen mode

SSR is now a first-class citizen with hydration support.


Building enterprise web applications? I create developer tools and data infrastructure. Email spinov001@gmail.com or check my Apify tools.

Top comments (0)