Angular v15 is here … do more, code less

Dibo
4 min readNov 21, 2022

--

Angular issues two major versions yearly, this year it introduced v14 and v15.

most updates in v15 were introduced already in v14 as experimental but became stable APIs.

in addition some new interesting features.

make sure you follow our pretty newsletter developers talks on LinkedIn

RIP NgModule

Angular was born with its famous NgModule, all parts of Angular including Components always tied with Angular modules.

Angular 14 came with a module-less component feature known as stand-alone components.

this feature was experimental in v14 and became stable in v15.

the stand-alone component behaves as if it is a 2-in-1 NgModule and a component.

it can import modules and even another stand-alone component.

@Component({
selector: 'app-my-component',
standalone: true,
imports: [CommonModule, OtherComponent]
template: `
<h2>Today is {{today | date}}</h2>'
<app-other-component></app-other-component>
`
})
export class MyComponent {}

bye bye class-based Router Guards

as you start your journey with Angular, you find classes everywhere as if Angular doesn’t know about functions.

everything in Angular is a class. Router Guards are not an exception.

Angular v15 introduces the new functional Router Guards.

not only this, but also you can pass data to it directly without the need to use ActivatedRouteSnapshot to obtain it.

you may ask “how then we can inject the dependencies without classes”?! see the example below.

the old-school Guards.

@NgModule({
imports: [
RouterModule.forRoot(
[
{ path: '/home', component: HomeComponet, canActivate: [LoggedInGuard] }
]
),
],
exports: [RouterModule],
})
export class AppRoutingModule {}
// ----> Guards are classes
@Injectable({ providedIn: 'root' })
export class LoggedInGuard implements CanActivate {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
// ----> we need ActivatedRouteSnapshot to read the data
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
if (!await this.authService.isLoggedIn()) {
return true;
}
return this.router.navigateByUrl('/home');
}
}

the new-style Guards:

provideRouter([
{ path: '/home', component: HomeComponet, canActivate: [isLoggedIn(['LOGGED_IN'])] }
])
// --> now we can access `data` directly without ActivatedRouteSnapshot
function isLoggedIn(data: string[]) {
// --> also, we can inject dependencies with `inject()`
if (!await inject(AuthService).isLoggedIn()) {
return true;
}
return inject(Router).navigateByUrl('/home')

automatic directives

imagine you created a RoundedCorners directive, and you need to apply it to all MyComp instances, you need to do so manually.

<my-comp RoundedCorners></my-comp>
<my-comp RoundedCorners></my-comp>
<my-comp RoundedCorners></my-comp>

in Angular v15 you can do it automatically, by instructing the component to automatically apply all directives you like.

@Component({
selector: 'my-comp',
standalone: true,
imports: [RoundedCornersDirective],
// 👇 This is how you assign directives to the component in Angular 15
hostDirectives: [RoundedCornersDirective],
template: '<button>Hello World</button>';
})
export class MyComponent {}

now, each time you call <my-comp></my-comp> the RoundedCorners applied automatically.

you even can combine multiple directives into a single directive and add your own species by extending their functionality

@Directive({
selector: '[applyStyles]',
hostDirectives: [RoundedCornersDirective, HighlightDirective, UnderlineDirective]
})
export class ApplyStylesDirective {}

instead of <my-comp roundedCorners highlight underLine>, just do <my-comp applyStyles>

Optimized images directive

Angular and Aurora (the chrome team) collaboration resulted in a new images directive that can optimize and lazy-load images on web pages.

all you need to replace the attribute src with the directive ngSrc

@Component({
standalone: true,
// 👇src -> ngSrc
template: '<img [ngSrc]="src"/>'
imports: [NgOptimizedImage],
})
class MyComponent {
@Input() src: string;
}

we will miss the fucking messy Stack Trace

one of the most painful dilemmas in Angular is its stack trace, most developers never understand what the trace tries to tell them !!

‘Finally, Angular felt the people’s pain!

all traces that come from node_modules and we never concern about are gone away.

only errors in your own code base are shown up.

// Error from Angular 14
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

here we can know that zone.js complains about something, but only God knows what exactly is it.

// The same Error from Angular 15
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 we can know that the click listener in line 4 of our AppComponent’s template has a problem.

Material MDC

Material design and CDK toolkits also received updates.

Angular Material components were refactored based on Material Design Components for Web (MDC) to be aligned with the Material Design specifications.

most components are rewritten from scratch.

Due to the new DOM and CSS, you will likely find that some styles in your application need to be adjusted, especially if you override the styles of the internal classes too much.

to migrate your material into v15 via the schematics tool run ng update @angular/material^15`` then, run the migrating tool ng generate @angular/material:mdc-migration`

CDK offered the new Listbox module that provides directives to help create custom Listbox interactions based on the WAI-ARIA Listbox pattern.

es-build support

now, Angular support using es-build to build the project.

you just need to make a slight modification to your builder configuration in the angular.json file

from:

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

to:

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

TypeScript 4.9

Angular v15 now supports TypeScript 4.9.

the new features and updates of Typescript will be covered in the next newsletter vol. follow me.

--

--

Dibo
Dibo

Written by Dibo

Software instructor and team lead

Responses (2)