Signals

Angular v19 No Signals Edition

Angular v19 is all the rage these days! Of course, everyone talks about SSR improvements, like incremental hydration, linkedSignal and resource/rxResource APIs, and you can already find some articles about them in here: * Article on Resource API by myself * Article about porting the Resource API to

Angular v19 No Signals Edition — Signals article by Armen Vardanyan on Angular In Depth
Angular v19 No Signals Edition — Signals article by Armen Vardanyan on Angular In Depth
On this page · 9 sections

Angular v19 has generated considerable excitement recently. Most of the conversation centers on server-side rendering enhancements, including incremental hydration, the linkedSignal and resource/rxResource APIs. You can find dedicated write-ups on those topics already:

  • A detailed piece on the Resource API by me
  • An article by Eduard Krivanek about adapting the Resource API for older Angular versions using Observables

That said, this piece steps away from the headline features to explore a handful of quieter, yet valuable, refinements in v19 that may have gone unnoticed.

Let us get started with the no-signals edition.

Streamlined application initializer setup

Angular applications often need to execute certain logic before the app initializes. This could involve fetching essential configuration or restoring previous state from localStorage.

Regardless of the use case, having a clean way to run such functions matters. Prior to v19, the standard approach was the APP_INITIALIZER token:

export function initializeApp(configService: ConfigService) {
  return () => configService.loadConfig();   

}

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    ConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: initializeApp,
      deps: [ConfigService],
      multi: true
    }
  ]
};

This method worked, though it required a fair amount of boilerplate. To simplify the process, Angular now offers the provideAppInitializer function for registering initializers:

import { provideAppInitializer } from '@angular/core';

export function initializeApp() {
  const configService = inject(ConfigService);
  configService.loadConfig();
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    ConfigService,
    provideAppInitializer(initializeApp)
  ]
};

This approach is significantly cleaner. The initializer function executes within an injection context, so inject works as expected. Multiple calls to provideAppInitializer can register several initializers.

The same pattern applies to PLATFORM_INITIALIZER and ENVIRONMENT_INITIALIZER, which are now superseded by providePlatformInitializer and provideEnvironmentInitializer.

Heads up: the old APP_INITIALIZER, PLATFORM_INITIALIZER and ENVIRONMENT_INITIALIZER tokens are still functional, but they are deprecated and slated for removal in a future release.

Passing data to router outlets

Components nested inside other components typically receive data through input properties. Routed components can also access URL parameters, query parameters, and resolved data via input bindings.

This is well-known. But what about cases where the data originates from a component higher up the tree and is not part of the routing state? For example, imagine a secondary router outlet for a sidebar, where the menu items live in an array within the component that hosts the outlet. How can that data be passed down?

Thankfully, v19 introduces a new input named routerOutletData on the <router-outlet> directive, enabling data to flow directly to components rendered through that outlet.

Providing the data is straightforward:

@Component({
  template: `
    <router-outlet routerOutletData="sidebarItems"></router-outlet>
  `,
})
export class NavigationComponent {
    readonly #navigationService = inject(NavigationService);
    sidebarItems: NavigationData = {
        items: this.#navigationService.getSidebarItems(),
    };
}

Inside any child route's component, that data is then accessible:

@Component({
  template: `
    @for (item of routerOutletData().sidebarItems) {
        <app-sidebar-item [item]="item" />
    }
  `,
})
export class SidebarComponent {
  routerOutletData = inject(ROUTER_OUTLET_DATA) as Signal<NavigationData>;
}

The standout aspect here is that the injected data arrives as a Signal. This means it automatically updates when the source data changes in the parent, enabling seamless communication between routed components without relying on global state management or injecting services everywhere.

Using the typeof operator in templates

There has been ongoing discussion about introducing lexical scope in Angular templates, which would allow direct access to global variables, functions, and built-in objects like Math and Date. While that larger feature is still on the horizon, a more focused enhancement has landed thanks to Matthieu Riegler, who contributed a PR enabling the typeof operator in templates.

This proves handy when dealing with uncertain data types. For instance, a third-party library might return an object on success or a string error message on failure, yielding a Result | string type. Since the library is out of our control, handling that error case often leads to verbose logic.

With typeof, checking the result type and responding appropriately becomes trivial:

@let result = getResult(data);
@if (typeof result === 'string') {
    <span>Error: {{ result }}</span>
} @else {
    <span>Success: {{ result.value }}</span>
}

This also brings type safety directly into the template, which is a nice upgrade.

Resolvers can now redirect

Guards often follow a pattern: check a condition, then either return true to allow navigation or return a redirect command to send the user elsewhere. This is a handy feature, yet it was never available to resolvers. Previously, redirection inside a resolver required injecting the router and manually calling navigate or navigateByUrl, which broke the declarative style of resolver logic.

Starting with Angular v19, that changes. Resolvers can now return RedirectCommand objects. For example, if a product resolver fails to find the requested item, redirecting to a "Not found" page is now a simple affair:

export const productResolver: ResolveFn<Product> = ({ paramMap }) => {
  const router = inject(Router);
  const productService = inject(ProductService);
  const id = paramMap.get('id') ?? 0;
  return productService
    .getProductById(+id)
    .pipe(
      catchError(() =>
        of(new RedirectCommand(router.createUrlTree(['/error']))),
      )
    );
};

That small block of code handles a relatively complex flow, greatly enhancing the developer experience.

Empty styles now default to ViewEncapsulation.None

Angular applies a mechanism called ViewEncapsulation to component styles, ensuring that selectors used in one component do not clash with those in another. It works by adding unique attributes to the template elements. A rendered Angular template often looks like this:

<p _ngcontent-ng-c3298008605="">
    <a _ngcontent-ng-c3298008605="" href="example.com" target="_blank" class="location">
        <mat-icon _ngcontent-ng-c3298008605="" role="img" svgicon="marker"
                class="mat-icon notranslate mat-icon-no-color" aria-hidden="true" 
                data-mat-icon-type="svg" data-mat-icon-name="marker"></mat-icon>
    </a>
</p>

Attributes such as _ngcontent-ng-c3298008605 are added to rendered elements to make them distinct, preventing style collisions across the app. If a component defines no custom styles, it makes sense to disable encapsulation entirely to avoid the overhead of adding those attributes. Previously, Angular would default to ViewEncapsulation.None only when styles or styleUrls were not set. If those properties were present but empty, this code:

@Component({
    template: `<p>Hello!</p>`,
    styles: ``,
})
export class AppComponent {}

would still generate the extra attributes, adding unnecessary work. Given that the Angular CLI creates component.scss files by default, and many developers leave them empty, this resulted in avoidable effort for both the compiler and the browser. This has been corrected in v19; when a component has no styles, the custom attributes are no longer generated.

Explicit this in templates

When writing template expressions, we typically refer to component properties without the this keyword, as the compiler infers the component instance. So, <span>{{ title }}</span> is automatically read as <span>{{ this.title }}</span>. Most of us are familiar with this behavior. However, it is less known that explicitly writing this in templates is actually allowed, even if it often seems pointless.

There are, though, two situations where this could be useful. The first involves working around a TypeScript limitation in templates that use signals. Consider having to extract the value of a possibly nullable signal:

@if(someSignal()) {
  <!-- this won't work despite checking for null
       as signals are functions and checking the return value 
       for `null` once does not (from TypeScript's perspective) 
       guarantee that the signal will return a non-null value in the future,
       so we are forced to add non-null assertion everywhere we use the signal,
       even if we have checked it previously, so this code would be `{{ someSignal()!.someProperty }}` -->
  <span>{{ someSignal().someProperty }}</span>
}

To bypass this, one can assign the signal value to a local variable and use that variable in the template:

@let someSignalValue = someSignal()
@if(someSignalValue) {
  <span>{{ someSignalValue.someProperty }}</span>
}

Coming up with sensible names for every such signal is tedious, and suffixing everything with value is not ideal. The alternative is to create a local variable with the same name using this in the template:

@let someSignal = this.someSignal()
@if(someSignal) {
  <span>{{ someSignal.someProperty }}</span>
  <!-- no non-null assertion required here -->
}

While this particular feature is not new to v19, there is another scenario where this proves valuable: when a template variable collides with a component property name, such as in an ng-template with a local variable. One would expect this to work:

<ng-template #myTemplate let-title>
  <!-- refers to the local variable -->
  <span>{{ title }}</span>
  <!-- refers to the component property -->
  <span>{{ this.title }}</span>
</ng-template>

Surprisingly, until v19, the compiler treated both references as the local variable, even when this was explicitly used. This bug is now fixed, allowing us to clearly distinguish between local variables and component class properties when needed.

APIs now considered stable

Angular is undergoing a significant transformation, and several features remain tagged as experimental or in developer preview. Nevertheless, v19 marks a major milestone in stabilizing functionality (and yes, this section touches on signals). Here is a rundown of the APIs that have achieved stable status:

API Introduced in Version Status
@let syntax v18.1 Stable
takeUntilDestroyed operator v15 Stable
outputFromObservable/outputToObservable functions v18.2 Stable
input/output/model properties v18+ Stable
Signal-based queries (viewChild and so on) v18+ Stable
withRequestsMadeViaParent option for HttpClient v18 Stable

This is welcome news for the Angular community, as it means these features can be adopted confidently in production environments without fear of imminent breaking changes.

Note: an interactive guide is available to track the status of all Angular features.

Wrap-up

Angular releases, particularly major ones like v19, come packed with improvements. Some of these get overshadowed by the more prominent, justifiably hyped features. This article aimed to shine a light on the quieter but still impactful updates to help developers stay current.

For a complete overview, the official release notes are available on GitHub via this link.

A quick note

Modern Angular.jpeg

Angular evolves quickly, and staying on top of everything can feel overwhelming. Fortunately, help is at hand! Over the past 18 months, I have been writing a comprehensive Angular book.

Titled "Modern Angular," it covers all the exciting capabilities introduced in recent releases (v14–v18), including standalone components, improved inputs, signals, better RxJS integration, SSR, and more. If that piques your interest, you can find it here.

The book is now in production, with the print edition expected in the coming weeks. It is currently available in Early Access with all 10 chapters online. To stay informed about the print launch, follow me on Twitter or LinkedIn, where I will share news and promotions.


Angular v19 No Signals Edition — figure 2

Angular v19 No Signals Edition — figure 3
AV
Armen Vardanyan

Writes about RxJS, State, Dependency Injection. Active 2019–2026.

All 57 articles →