Angular 15: All About Major Features & Improvements

what's new in angular 15 features and improvements background with angular logo
Ajay Thakor
19-Dec-2022
Reading Time: 6 minutes

After the grand successful of angular 14, now it’s coming with latest stable version Angular 15 which was released on 16 November 2022 with the some exciting performance improvement features. Below are

An overview of major features & improvements in Angular 15

  1. Stable Standalone Components API
  2. Capability to Create Multi-Route Applications
  3. Image Directive (NgOptimizedImage) is now Stable
  4. Extended esbuild support
  5. Better stack traces
  6. Functional router guards
  7. Directive composition API
  8. The router unwraps default imports
  9. Stable MDC-based Components
  10. CDK Listbox

Let’s discuss new features of Angular 15 one by one in depth:

1. Stable Standalone Components API

We saw in previous version of angular, they introduced about Standalone components and APIs, which was a one of the remarkable feature of angular, it makes easier to create angular applications without using the NgModules. And now, release with latest angular v15, the API becomes stable and is no more in Developer Preview status.

With this newly obtained stability in v15, the Angular developer community has ensured that standalone APIs may function in sync with HttpClient, Angular Elements, and many more.

The standalone APIs allow you to bootstrap an application using a single component:

import {bootstrapApplication} from '@angular/platform-browser';
import {ImageGridComponent} from'./image-grid';

@Component({
  standalone: true,
  selector: 'photo-gallery',
  imports: [ImageGridComponent],
  template: `
    … <image-grid [images]="imageList"></image-grid>
  `,
})
export class PhotoGalleryComponent {
  // component logic
}

bootstrapApplication(PhotoGalleryComponent);

Using the imports function, we can also reference standalone directives and pipes.
There is no longer a requirement to declare components, directives, and pipes into NgModules; Instead, you can declare them as “standalone: true.”

Additionally, by writing import: [module name] inside the standalone component, you can now import NgModule directly.

2. Capability to Create Multi-Route Applications

Using the new router standalone APIs, we can now create the multi-route application.

Here’s how you can declare the root route:

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

Here, lazyRoutes are declared in the following way:

import {Routes} from '@angular/router';

import {LazyComponent} from './lazy.component';

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

and now, register the appRoutes in the bootstrapApplication call

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(appRoutes)
  ]
});

and now, register the appRoutes in the bootstrapApplication call

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(appRoutes)
  ]
});

Here, provideRouter API is the tree-shakable.


Bundlers can remove unused features from the router during the build process, resulting in an 11% reduction in code bundle size.

3. Image Directive (NgOptimizedImage) is now Stable

In V14.2, the NgOptimizedImage was released, allowing us to quickly adapt to the performance of loading images. It is stable as of Angular 15 right now. In a lighthouse lab test for picture loading, Land’s End used this function and introduce around a 75% improvement in LCP (Largest Contentful Paint).

Image Redirective now stable Angular 15: All About Major Features & Improvements
Source: blog.angular.io

The previous version of NgOptimizedImage had a lot of features and functionalities. The image directive now has a few additional features that were included with the Angular v15 changes.

Automatic srcset Generation:

This directory creates the srcset automatically, enabling us to upload the right-sized images whenever needed.
An image’s download time is shortened as a result.

Fill Mode [experimental]:

This mode eliminates the need to declare image dimensions and fills the image to the size of its parent container.
This mode is useful when we don’t know the image dimensions we need to migrate in order to use this directive.

with the use of NgOptimizedImage directive, We can directly use NgOptimizedImage directive in angular component or NgModule.

import { NgOptimizedImage } from '@angular/common';

// Include it into the necessary NgModule
@NgModule({
  imports: [NgOptimizedImage],
})
class AppModule {}

// ... or a standalone Component
@Component({
  standalone: true
  imports: [NgOptimizedImage],
})
class MyStandaloneComponent {}

when we are working with NgOptimizedImage directive within a component, then we need to replace the image src attribute with the ngSrc.

4. Extended esbuild support

Support for esbuild in ng build in v14 allows for faster build times and simplifies the pipeline.

Now, v15 includes experimental Sass, SVG template, file replacement, and ng build –watchsupport!

Update esbuild in angular.json

From

"builder": "@angular-devkit/build-angular:browser"

To

"builder": "@angular-devkit/build-angular:browser-esbuild"
GIF explaining esbuild bundler speed and angular 15 Extended esbuild support
Source: blog.angular.io

5. Better Stack Traces

With Angular v15, we can now quickly trace the code.
This is useful if we encounter errors, the stack trace to find where the error is occurring.

Better Stack Traces Angular 15: All About Major Features & Improvements
Source: blog.angular.io

As a result, the Angular v15 developer team introduces this feature to trace more of a development code rather than showing libraries it calls.
The error was difficult to find in the prior version, but it is simple to trace in version 15.

Below is the snippet for previous error indications:

ERROR Error: Uncaught (in promise): Error
Error
    at app.component.ts:18:11
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:25:1)
    at _ZoneDelegate.invoke (zone.js:372:26)
    at Object.onInvoke (core.mjs:26378:33)
    at _ZoneDelegate.invoke (zone.js:371:52)
    at Zone.run (zone.js:134:43)
    at zone.js:1275:36
    at _ZoneDelegate.invokeTask (zone.js:406:31)
    at resolvePromise (zone.js:1211:31)
    at zone.js:1118:17
    at zone.js:1134:33

The difficulty in understanding these ERROR snippets was:

1.The error message inputs were coming from third-party dependencies (Angular framework, zone.js, and RxJS)
2.No information about which user interaction encountered this bug.

Now in Angular v15 stack trace will be like below

ERROR Error: Uncaught (in promise): Error
Error
    at app.component.ts:18:11
    at fetch (async)  
    at (anonymous) (app.component.ts:4)
    at request (app.component.ts:4)
    at (anonymous) (app.component.ts:17)
    at submit (app.component.ts:15)
    at AppComponent_click_3_listener (app.component.html:4)

Now, these error messages include information about where the error occurred, allowing developers to go directly to that code section and fix it.

6. Functional Router Guards

We work on reducing the boilerplate in the guards using tree-shakable standalone router APIs.

Let’s use one example of defining guards, checking information, and determining whether the user has signed in or not to better grasp this concept:

@Injectable({ providedIn: 'root' })
export class MyGuardWithDependency implements CanActivate {
  constructor(private loginService: LoginService) {}

  canActivate() {
    return this.loginService.isLoggedIn();
  }
}

const route = {
  path: 'somePath',
  canActivate: [MyGuardWithDependency]
};

Here LoginService implements most of the logic and in the guard we only invoke isLoggedIn().
Even though the guard is pretty simple, we have lots of boilerplate code.

Now with the new functional router guards, we can refactor the code like below.

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

We have created the entire guard in the guard declaration.
The best thing about Functional Guards is that they are compostable. With its help, you can build factor-like functions, accepting a given configuration and returning a guard or function that resolves a matter.

7. Directive Composition API

By this feature, We can build the angular application with the help of code reusability and it also helps us with code reuse. This is made possible with the help of the angular compiler and also enables developers to enhance host elements with the directives.

The directive composition APIs only work with the standalone directives

@Component({
  selector: 'mat-menu',
  hostDirectives: [HasColor, {
    directive: CdkMenu,
    inputs: ['cdkMenuDisabled: disabled'],
    outputs: ['cdkMenuClosed: closed']
  }]
})
class MatMenu {}

The two directives HasColor and CdkMenu enhance MatMenu in the code above.
MatMenu helps us to reuse all the inputs, outputs, and related logic with HasColor and only logic and the selected input from the CdkMenu.

8. The Router Unwraps Default Imports

To further simplify the router and reduce boilerplate, the router now auto-unwraps default exports when lazy loading.

See the following LazyComponent:

@Component({
  standalone: true,
  template: '...'
})
export default class LazyComponent { ... }

Prior to this update, in order to lazy load a standalone component, you had to:

{
  path: 'lazy',
  loadComponent: () => import('./lazy-file').then(m => m.LazyComponent),
}

The router will now check for a default export and, if found, will utilise it automatically, simplifying the route declaration to:

{
  path: 'lazy',
  loadComponent: () => import('./lazy-file'),
}

9. Stable MDC-based Components

It was previously difficult to create reflector component-based angular material, but MDC has made it possible (Material design component for web)

The majority of the refactoring work in v15 has been done in the DOM and CSS sections. Following the new responsiveness update, some styles in the old Angular applications will require adjustments, particularly when CSS is overriding internal elements of the migrated components.

Many outdated component implementations have been deprecated in the most recent version of Angular, v15. Therefore, developers still have the choice to use the “legacy” import to retrieve them.

For example, we can retrieve the old mat-button implementation by importing its legacy button module.

import {MatLegacyButtonModule} from '@angular/material/legacy-button';

10. CDK Listbox

CDK, which stands for Component Dev Kit, provides various behaviour primitives and helps in the creation of UI components.

A new primitive called CDK Listbox was added to Angular v15, which allows developers to customise Listbox interactions drawn up by the WAI-ARIA Listbox pattern based on requirements.

CDK Angular 15: All About Major Features & Improvements
Source: blog.angular.io

Keyboard interactions, bidi layout support, and focus management are examples of behavioural interactions. Regardless of which one you use, each directive generates associated ARIA roles with their respective host elements.

To learn more about Angular 15 feature upgrades, visit update.angular.io, where you’ll receive detailed guidance on how to proceed.

Wrapping Up

In conclusion, Angular 15 is packed with advanced features and major improvements. It offers several enhancements that simplify the whole development process from a development perspective. The features and improvements improve safety and reduce backlogs, which has been a pain point for developers.