What’s new in Angular 15?

Alain Chautard
Angular Training
Published in
6 min readNov 30, 2022

--

Angular 15 was released in early November 2022. This version brings a lot of new features, more than what we’ve been used to with previous major versions of Angular.

Let’s dive into some of the significant new features one by one:

Directive composition API

Before Angular 15, we could use inheritance to create a directive extending another. While inheritance works, it is often more flexible to use composition instead, and that’s precisely what Angular 15 allows us to do.

How does that work? If we want to create a component or directive that automatically has one or more other directives applied to it, we can use the following syntax:

@Component({
selector: 'admin-menu',
template: 'admin-menu.html',
hostDirectives: [MenuBehavior],
})
export class AdminMenu { }

In the above example, the directive MenuBehavior will always be applied to the AdminMenu component, with no need to add any selector to the component’s host HTML element.

It’s even possible to forward inputs and outputs to these directives using the following syntax:

@Component({
selector: 'admin-menu',
template: 'admin-menu.html',
hostDirectives: [{
directive: MenuBehavior,
inputs: ['menuId: id'],
outputs: ['menuClosed: closed'],
}],
})
export class AdminMenu { }

The above code means that the id attribute on the admin-menu element becomes the menuId input of the MenuBehavior directive. Likewise, the closed output of the admin-menu element becomes the menuClosed output of MenuBehavior.

As a result, the AdminMenu component can be used like so:

<admin-menu id="top-menu" (closed)="logMenuClosed()">

And as you can see, the MenuBehavior directive is not “visible” in that HTML. So no extra selector is needed.

The complete documentation can be found here for more information on the directive composition API.

Angular Certification Exam

Stable standalone APIs

The standalone component API introduced in Angular 14 as a developer preview is now fully stable and out of developer preview.

Note that you can generate a standalone component with the new Angular CLI option:

ng g component MyComponent --standalone

This feature fully enables module-less Angular applications. For example, we can bootstrap such an application using a standalone component with the following syntax:

bootstrapApplication(MyStandaloneAppComponent);

Another consequence of that change is that lazy-loading can be done without modules. Instead, we can lazy load a bunch of routes using the following syntax:

export const appRoutes: Routes = [{
path: 'lazy',
loadChildren: () => import('./lazy/lazy.routes')
.then(routes => routes.lazyRoutes)
}];

With lazyRoutes being declared in lazy.routes.ts like so:

import {Routes} from '@angular/router';
import {LazyComponent} from './lazy.component';
import {LazyDashboardComponent} from './lazy-dashboard.component';

export const lazyRoutes: Routes = [
{path: '', component: LazyComponent},
{path: 'dashboard', component: LazyDashboardComponent},
];

No modules are involved!

Image optimization directive (NgOptimizedImage)

Images are vital when it comes down to the performance of any web application. But, unfortunately, we can try to optimize our Javascript code and save kilobytes here and there, only to see our efforts ruined by massive images that take way too much time to get downloaded.

The image directive is designed to help with that.

What it does:

  • Intelligent lazy loading: Images that are invisible to the user on page load are loaded later when the user scrolls down to that page section.
  • Prioritization of critical images: Load the essential images first (header banner, for instance)
  • Optimized configuration for popular image tooling: If you’re using a CDN, the directive will automatically pick the proper image size from that CDN, optimizing download sizes based on how many pixels of that image will be displayed on the screen.
  • Built-in errors and warnings: Besides the above built-in optimizations, the directive also has built-in checks to ensure that developers have followed the recommended best practices in the image markup.

The directive can be used with the ngSrc attribute instead of just src:

<img ngSrc="image.png" >

The NgOptimizedImage directive is part of the @angular/common module, just like ngFor and ngIf, so it’s already part of your toolkit if you use those directives.

It can also be used as a standalone directive without importing CommonModule. Its official documentation and more information about the magic behind that directive can be found here.

Better stack traces for faster debugging

Stack traces are vital in debugging our code faster. So the Angular team keeps working on more ways to make errors and stack traces more focused, explicit, and relevant.

Before, stack traces would include Zone.js, which doesn’t help us with the code we’re writing:

With Angular 15, only the relevant parts of the stack trace are now displayed:

And soon, it’s going to get even better, with the last line above being replaced with:

at <button (click)="submit()">    (app.component.html:4)

That way, we’ll know instantly which line of code triggered the issue.

Angular CDK Listbox

There are several ways to create dropdowns and lists of selectable items using HTML, from the select tag to completely custom options using div or ul and li.

What we gain in terms of customization (styling, validation rules) usually becomes a loss in terms of accessibility.

By using @angular/cdk/listbox, we get all the expected behaviors for an accessible experience, including bidirectional layout support, keyboard interaction, and focus management:

Some examples can be found here.

Language service improvement

The Angular language service keeps improving, translating into a better IDE experience for all developers.

Angular 15 brings automatic component imports when a component is used for the first time and hasn’t been imported before (either as a standalone component or from a module).

Functional router guards

This is the line of code that changes the way we think about router guards in Angular:

const route = {
path: 'admin',
canActivate: [() => inject(LoginService).isLoggedIn()]
};

No need to create a specific class that implements the canActivate interface. A simple function can do the job now!

On a similar note, lazy-loading can be done at the component level with standalone components using the following syntax:

{
path: 'lazy',
loadComponent: () => import('./my.component').then(m => m.MyComponent),
}

The above example can be simplified even further when the component definition is the default export in its file. This is called auto-unwrap and results in the following shorter configuration:

{
path: 'lazy',
loadComponent: () => import('./my.component'),
}

Default format options for date pipe

Finally, since most applications use a consistent date format throughout all screens and components, we can now define that default format once and for all with a new injectable token called DATE_PIPE_DEFAULT_OPTIONS:

providers: [
{
provide: DATE_PIPE_DEFAULT_OPTIONS,
useValue: { dateFormat: 'shortDate' }
}
]

The official post from the Angular team with even more information can be found here: https://blog.angular.io/angular-v15-is-now-available-df7be7f2f4c8

My name is Alain Chautard. I am a Google Developer Expert in Angular, as well as a consultant and trainer at Angular Training where I help web development teams learn and become comfortable with Angular.

If you need any help with web development, feel free to get in touch!

If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to Medium here.

--

--