Angular's Declarative Change

Angular Firebase profile imageAntonio Cardenas

Antonio Cardenas for Angular Firebase

Posted on Oct 19

11111

Angular’s Declarative Change

#angular#typescript#webdev#spanish

How the New Folder Structure Changes Everything

If you haven’t updated to version 20, the first and second parts of this guide can help you understand what changes and how to update.

Part 1: The Update Itself :hammer_and_wrench:

First, let’s tackle the basics. Before running any command, make sure your environment is ready.

Prerequisites

  • Node.js: v20.11.1 or later.
  • TypeScript: v5.8 or later.
  • Project Backup: Make sure to commit all your current changes in Git. Seriously.

The Update Command

Once you’ve confirmed your Node.js version, run the command that fits your project.

For a standard Angular project:

ng update @angular/cli @angular/core

If you use Angular Material:

ng update @angular/cli @angular/core @angular/material

The update process will run, but you’ll likely encounter your first obstacle right away.

Part 2: What Breaks Immediately :stop_sign:

Unlike previous updates, Angular 20 introduces a significant change that will stop your build.

1. The Full Story Behind the Removal of Karma

The first thing you’ll notice is that ng test will fail. This isn’t just an error; it’s a fundamental change in Angular’s build tools. With Angular 20, the default build package changes from @angular-devkit/build-angular to the new @angular/build. This new package does not include Karma. The web ecosystem has moved towards faster, more modern test runners like Vitest and Jest, and Karma had become a bottleneck.

Temporary Solution:

To run your tests without migrating everything today, you need to manually reinstall the old build tool. This forces the CLI to use the old compiler that is still compatible with Karma.

npm install @angular-devkit/build-angular --save-dev

This is a compatibility bridge. The message from the Angular team is clear: start planning your migration to Jest or Vitest soon.

2. browserslist and Browser Support

Here’s a minor detail that might surprise you. Angular 20 officially no longer supports Opera. If you have “Opera” listed in your .browserslistrc file, your build may fail or throw warnings. Remove it to resolve the issue.

Part 3: The New Architecture :classical_building:

Beyond the major changes, Angular 20 drives a more modern, explicit, and scalable architecture.

1. Standalone Components by Default.

New projects generated with ng new are now standalone (independent) by default. This marks a fundamental architectural shift away from NgModules. By listing dependencies directly in the imports array of a component, each component becomes self-contained.

What does this change imply?

  • Clearer and more defined architecture: You know exactly what each component needs.
  • Improved tree-shaking: Leads to smaller, more optimized packages.

To migrate your existing projects, you can run the standalone migration schematic:

ng generate @angular/core:standalone

2. A Folder Structure That Tells a Story

A good folder structure not only contains files but tells you what the application does. It’s worth emphasizing Angular’s official style guide, as its recommendations are based on years of community experience. This philosophy, detailed in its file structure reference page, is often summarized with the acronym LIFT (Locate, Identify, Flat, Try to be DRY):

  • Locate (Locate) your code easily.
  • Identify (Identify) what a file does at a glance.
  • Flat (Flat): Keep the structure as flat as possible.
  • Try to be DRY: Don’t Repeat Yourself.

The main takeaway is organize by feature, not by type. Instead of a components folder and a services folder, create folders for features like user-profile or product-list.

Let’s use a practical example for an e-commerce application:

src/app/
├── core/
│   ├── auth/          # Authentication logic used everywhere
│   │   ├── auth-store.ts
│   │   └── auth-interceptor.ts
│   └── layout/        # The application shell: navigation bar, footer
│       ├── navbar.ts
│       └── footer.ts
│
├── features/
│   ├── products/      # Everything to browse products
│   │   ├── product-list.ts
│   │   ├── product-details.ts
│   │   └── product-search.ts
│   │
│   └── cart/          # The shopping cart feature
│       ├── cart-store.ts
│       ├── cart-view.ts
│       └── add-to-cart.ts
│
└── shared/            # Reusable "dumb" components and utilities
    └── ui/
        ├── button.ts
        ├── spinner.ts
        └── price.pipe.ts

Why Does This Work?

  1. core :classical_building: (Provide Once): Services and components that the application needs to run, loaded only once (AuthStore, Navbar).
  2. Features :sparkles: (What Your App Does): The heart of your application. Each folder is a self-contained feature.
  3. shared :recycling_symbol: (Reusable Building Blocks): “Dumb” components, pipes, and directives that know nothing about the features they are used in (Button, Spinner). They are imported by feature modules.

3. The New Naming Convention

Angular 20 introduces a new official naming convention that eliminates traditional suffixes like .component.ts or .service.ts.

Old Nomenclature New Nomenclature Intention
user-profile.component.ts user-profile.ts User Interface Component
auth.service.ts auth-store.ts State Management
highlight.directive.ts highlight.ts Directive
user-api.service.ts user-api.ts HTTP Client

The goal is to focus on the intention of the file rather than its technical type. A class that handles state is a “store” (store), and one that makes HTTP requests is an “api”. This makes the purpose of your code much clearer, especially in a feature-based folder structure.

Part 4: The New Tools and Syntax :rocket:

Finally, let’s look at the new tools that will improve your daily development.

1. Control Flow is More Than Syntactic Sugar.

The new @for block replaces *ngFor and is a significant improvement.

Old Syntax:

<div *ngFor="let item of items; trackBy: trackItemById">
  {{ item.name }}
</div>

New Syntax:

@for (item of items; track item.id) {
  <div>{{ item.name }}</div>
} @empty {
  <div>No items to display.</div>
```Key Improvements:

* `track` is **required**, enforcing a performance best practice that was often overlooked.
* The built-in `@empty` block cleans up templates by eliminating the need for a separate `*ngIf`.

You can use the CLI to automatically refactor your templates:

```bash
ng generate @angular/core:control-flow

2. Zoneless: Escaping the “Change Detection Magic”.

Although still experimental, the path to a zoneless Angular is becoming clearer. In a zoneless world, the user interface only updates when you explicitly tell it to.

Signals are the primary tool for this.

mySignal.set(newValue);

This line directly tells Angular to update only the specific parts of the DOM that depend on that signal. It’s a surgical, predictable, and high-performance approach that eliminates the overhead and unpredictability of Zone.js.

Conclusion

Angular 20 is a significant release. The update requires manual intervention for testing, but it drives the framework toward a more modern, explicit, and high-performance future. By adopting standalone components, a feature-based architecture, and the new control flow, you’re not just updating—you’re preparing your application for the next era of web development.