Angular is one of the most popular front-end frameworks. It allows you to create single-page applications using TypeScript. The latest version (v15) was officially released a few days ago and introduced considerable changes, descriptions of which you can find below.
Standalone components
In the previous version (v14) standalone components APIs to build applications without using NgModules were introduced. From now on, these APIs moved from developer preview and are now part of the stable API surface. Also, they will be evolving gradually following the semantic versioning.
Standalone components work in Angular, and now they also are fully operational in HttpClient, Angular Elements, router and more. These APIs allow you to bootstrap an application using a single component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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);
Multi-Route Application
In the new version you can build a multi-route application using the new router standalone APIs. Here is an example of how to do achieve that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
export const appRoutes: Routes = [{ path: 'lazy', loadChildren: () => import('./lazy/lazy.routes') .then(routes => routes.lazyRoutes) }]; Next, declare the lazyRoutes in this way import {Routes} from '@angular/router'; import {LazyComponent} from './lazy.component'; export const lazyRoutes: Routes = [{path: '', component: LazyComponent}]; And in the end, register appRoutes in the bootstrapApplication call: bootstrapApplication(AppComponent, { providers: [ provideRouter(appRoutes) ] });
Another benefit of the provideRouter API is that it’s tree-shakeable! Bundlers can remove unused features of the router at build-time. This allows to decrease the size of the router code in the application bundle.
Router unwraps default imports
Router now auto-unwraps default export when lazy loading, to make router simpler and reduce the amount of code.
Now we can add the route:
1 2 3 4
{ path: 'lazy', loadComponent: () => import('./lazy-file'), }
And thus router will look for a default export and use it automatically
Functional router guards
While working on standalone router APIs, boilerplate in guards have also been reduced. To implement the guard, which will verify if a user is logged in, you can use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@Injectable({ providedIn: 'root' }) export class MyGuardWithDependency implements CanActivate { constructor(private loginService: LoginService) {} canActivate() { return this.loginService.isLoggedIn(); } } const route = { path: 'somePath', canActivate: [MyGuardWithDependency] }; The guard invokes isLoggedIn() only, while the all logic is inside the service, so the guard itself is simple. We have some code that isn't needed there, though. With the new functional router guards, you can put this code above instead. const route = { path: 'admin', canActivate: [() => inject(LoginService).isLoggedIn()] };
In this part of the code, we implement the guard directly in the declaration, minimizing the code as a result.
Directive Composition API
Due to the large number of requests on GitHub, directives have been added to the host element - it's a new level of code reusability. The directive composition API brings up code usability, and it's made possible thanks to their compiler only.
The directive composition API only works with standalone directives.
1 2 3 4 5 6 7 8 9
@Component({ selector: 'mat-menu', hostDirectives: [HasColor, { directive: CdkMenu, inputs: ['cdkMenuDisabled: disabled'], outputs: ['cdkMenuClosed: closed'] }] }) class MatMenu {}
In the above example we extend MatMenu with two directives: HasColor and CdkMenu. For the first directive reuses all inputs and outputs and related logic, while for the CdkMenu uses only logic and specified inputs
This feature can remind you of multiple inheritance known from other languages. The difference is the mechanism for the resolution of name conflicts, and the fact that it is now applicable to user interface primitives.
Image directive
In current version of Angular image, the directive is stable - first time introduced in cooperation with Chrome Aurora in 14.2 as developer preview. Some features for the image directives were also added in this version.
Automatic srcset generation: the directive ensures that an appropriately sized image is requested by generating the srcset attribute for you. This can reduce download times for your images.
Fill mode - it's experimental, causes the image to fill its parent container, without the need to specify the image width and height. It's useful if you don’t know the sizes of your images or if you’d like to migrate CSS background images to use the directive.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
NgOptimizedImage can be used directly in your 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 {}
To use in the component, you now have to add ngSrc instead of src and specify the priority attribute to optimize the speed from the LCP images.
Better stack traces
Based on developers' feedback, Angular decided to improve error massages. Therefore, they have started cooperation with Chrome DevTools to resolve the issue. In the new version the Angular application debugging process has been simplified.
Before those changes, one line with the error message was related to developer code and the other came from related libraries. There was also no information provided about what interaction caused the error.
Now errors from node_modules are ignored, thanks to the addition of source maps by Angular CLI. They also worked on async stack tagging API to allow combining independent, scheduled async tasks into one stack trace.
This is an example of new error messages. Here you can track all the way from click button to error.
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)
The release of MDC-based components to stable
Refactoring material component based on Material Desing Components has been done. Angular team worked on adjusting own component and improved the accessibility and compatibility of Angular Material Design Specifications. This makes it possible to use Material 3.
Lots of changes were made in DOM and CSS, and consequently some styles should be adjusted, especially individual styles that override the internal elements of the migrated components.
All previous implementations of material component have been cut out. If you want to still use current components, you should change the import to legacy e.g.:
import {MatLegacyButtonModule} from '@angular/material/legacy-button';
More improvements in components
The range selection issue has been fixed, and additionally all components have an API to customize density. New version of components has a lot of accessibility improvements e.g., better contrast, increased touch area and better ARIA semantics.
CDK Listbox
The Component Dev Kit offers a set of behavior primitives for building UI components. In Angular 15, a new primitive named CDK Listbox appeared.
The @angular/cdk/listbox module helps developers to create custom listboxes with interactions based on the WAI ARIA listbox pattern. This component supports interactions with keyboard, bidi layout, and focus management to ensure accessible experience. All of directives apply related ARIA roles to their host element
Improvements in the experimental esbuild support
Angular 14 came with experimental support for esbuild in Ng build to enable faster build times and simplify pipeline.
In Angular 15, we have experimental support for the ng build --watch, Sass, SVG template and file replacement.
To use it, update your builder in angular.json to:
"builder": "@angular-devkit/build-angular:browser-esbuild"
Automatic imports in language service
Now you can automatically import components that you are using in a template that weren't added to component or NgModule.
CLI improvements
In the Angular CLI support for standalone components has been introduced. Now you can generate a new one with the command below:
ng g component –standalone
You can define the target ECMAScript version in .browserlist, to reduce the configuration overhead.
The configuration was also reduced by removing test.ts, polyfills.ts and some environments. Now you can specify that in angular.json
Summary
With both previous changes and the removal of Angular legacy compiler, this update makes a lot of adjustments to extend stability and supportability, which improves developer efficiency.
Launching Ivy in 2020 was the best decision, which allowed to implement lot of improvements e.g., NgModules. These makes pick up Angular is simpler and easier.
The angular team is working on improvements in the server-side rendering pipeline and reactivity while bringing high quality improvements everywhere.
So don't wait - update your angular application to the latest version and have fun coding!