GGoogle team released the latest version of Angular – Angular 17 on November 6, 2023, creating a significant milestone for the front-end development.
Features and
Updates:
1.
Angular 17 is
the highly anticipated release for the community, bringing many new
exciting features, updates, and improvements.
2.
New Syntax
for Control Flow in Templates - new @if, @switch, @for, @case, @empty @end
control flow syntax
3.
Deferred
Loading - @defer partial template
4.
The Angular
signals API
5.
Angular SSR
and client hydration
6.
Automatic
Migration to Build-in Control Flow
7.
Build
Performance with ESBuild
8.
By default,
set this newly generated component as a standalone, and now we don't have an app
module file. To use (ng new angular17 --standalone=false) to get the app module
file.
9.
Got a new
brand logo
10.
Got a new dev
documentation site
11.
ng update:
update existing applications to Angular 17 using ng update command
12. Super-Fast
Built-in control flow and the
NgIf, NgSwitch and NgFor structural directives:
- The @if block replaces *ngIf for expressing conditional parts of the UI.
- The @switch block replaces ngSwitch with major benefits.
- The @for block replaces *ngFor for iteration, and has several differences compared to its structural directive NgFor predecessor.
- The track setting replaces NgFor's concept of a trackBy function.
New Syntax for Control Flow in Templates - new @if, @switch, @for, @case, @empty @end control flow syntax:
Let’s look at a one-by-one
comparison with *ngIf:
Before Angular 17,
<div *ngIf="loggedIn; else guestUser">
The user is logged in the
application
</div>
<ng-template #guestUser>
The user is not logged in
the application
</ng-template>
With the built-in if statement,
this condition will look like:
From Angular 17,
The @if block conditionally displays its content when its condition expression is true.
Let's see the example, here ‘a’ and ‘b’ are two of the variables that are used in the conditionals @if block.
@if (a > b) {
{{a}} is greater than {{b}}
}
The @if block might have one or more associated @else blocks. Immediately after an @if block, you can optionally specify any number of @else if blocks and one @else block:
@if (a > b) {
{{a}} is greater than {{b}
} @else if (b > a) {
{{a}} is less than {{b}}
} @else {
{{a}} is equal to {{b}}
}
The improved ergonomics is even
more visible with *ngSwitch:
The new control flow enables
significantly better type-narrowing in the individual branches in @switch, which is impossible in *ngSwitch.
Before Angular 17,
<div [ngSwitch]="accessLevel">
<admin-dashboard
*ngSwitchCase="admin"/>
<moderator-dashboard
*ngSwitchCase="moderator"/>
<user-dashboard
*ngSwitchDefault/>
</div>
Which with the built-in control
flow turns into:
From Angular 17,
The syntax for @switch is very similar to @if statement. See what the basic looks like:
@switch (condition) {
@case (caseA) {
Case A.
}
@case (caseB) {
Case B.
}
@default {
Default case.
}
}
The value of the conditional expression is compared to the case expression using the === operator.
The @switch does not have fall through, so you do not need an equivalent to a break or return statement.
The @default block is optional and can be omitted. If no @case matches the expression and there is no @default block, nothing is shown.
Built-in for loop -
From Angular 17: The @for repeatedly renders the content of a block for each item in a collection. The collection can be represented as any JavaScript alterable but there are performance advantages of using a regular Array.
A basic @for loop looks like:
@for (item of items; track item.id) {
{{ item.name }}
}
What Is ‘track’?
The track setting replaces NgFor's concept of a trackBy function.
The value of the ‘track’ expression determines a key used to associate array items with the views in the DOM.
Inside @for contents, several implicit variables are always available:
1. $count: Number of items in a collection iterated over
2. $index: Index of the current row
3. $first: Whether the current row is the first row
4. $last: Whether the current row is the last row
5. $even: Whether the current row index is even
6. $odd: Whether the current row index is odd
These variables are always available with these names, but can be aliased via a let segment:
@for (item of items; track item.id; let idx = $index, e = $even) {
Item #{{ idx }}: {{ item.name }}
}
@empty block
You can optionally include an @empty section immediately after the @for block content. The content of the @empty block displays when there are no items:
A basic @empty block looks like:
@for (item of items; track item.name) {
<li> {{ item.name }} </li>
} @empty {
<li> There are no items. </li>
}
Deferrable Loading: @defer partial template :
Now let’s talk about the future of
lazy loading! The new deferrable views, allow you to lazily load the list of
comments and all their transitive dependencies with a single line of
declarative code:
@defer {
<comment-list />
}
The most incredible part is that this all happens via a compile-time transformation: Angular abstracts all the complexity by finding components, directives, and pipes used inside of a @defer block, generating dynamic imports, and managing the process of loading and switching between states.
Angular makes using
IntersectionObservers as simple as adding a deferrable view trigger!
@defer (on viewport) {
<comment-list />
} @placeholder {
<!-- A placeholder content
to show until the comments load -->
<img
src="comments-placeholder.png">
}
Deferrable views offer a few more triggers:
1.
on idle —
lazily load the block when the browser is not doing any heavy lifting
2.
on immediate
— start lazily loading automatically, without blocking the browser
3.
on
timer(<time>) — delay loading with a timer
4.
on viewport
and on viewport(<ref>) — viewport also allows to specify a reference for
an anchor element. When the anchor element is visible, Angular will lazily load
the component and render it
5.
on
interaction and on interaction(<ref>) — enables you to initiate lazy
loading when the user interacts with a particular element
6.
on hover and
on hover(<ref>) — triggers lazy loading when the user hovers an element
7.
when
<expr> — enables you to specify your own condition via a Boolean
expression
The Angular
signals:
A signal is a wrapper around a
value that can notify interested consumers when that value changes. Signals can
contain any value, from simple primitives to complex data structures.
A signal's value is always read
through a getter function, which allows Angular to track where the signal is
used.
Signals may be either writable or read-only.
The angular signal () function
creates a signal. This function takes two input parameters.
1.
Initial value
2.
Optional Equality function.
Whenever there is a change or
update in signal value, it propagates the change to effect () such that you can
run custom code inside the effect () function.
Using the equality function, you can write your own business logic to determine whether change should propagate to effect () or not.
Angular Signals multiple examples here...
Today, we bring server-side rendering (SSR) and static-site generation (SSG or prerendering) closer to developers with a prompt in ng new:
Alternatively, you can enable SSR in new projects with ng new myAppName --ssrNew
@angular/ssr package:
To add hybrid rendering support to your existing application run: ng add @angular/ssr
The above command
1)
will generate
the server entry point
2)
To add SSR
and SSG build capabilities
3)
To enable
hydration by default
The @angular/ssr provides
equivalent functionality to @nguniversal/express-engine which is currently in
maintenance mode. If you’re using the express-engine, Angular CLI will
automatically update your code to @angular/ssr.
New lifecycle
hooks:
To improve the performance of Agular’s SSR and SSG, in the long term, we would like to move away from DOM emulation and direct DOM manipulations.
To enable this, we developed a set
of new lifecycle hooks:
1)
afterRender —
register a callback to be invoked each time the application finishes rendering
2)
afterNextRender
— register a callback to be invoked the next time the application finishes
rendering
Example of
using afterRender and afterNextRender:
@Component({
selector: 'my-chart',
template: `<div #chart>{{
... }}</div>`,
})
export class MyChart {
@ViewChild('chart') chartRef:
ElementRef;
chart: MyChart|null;
constructor() {
afterNextRender(() => {
this.chart = new
MyChart(this.chartRef.nativeElement);
}, {phase:
AfterRenderPhase.Write});
}
}
Build
Performance with ESBuild: The new Application Builder
Angular has always had different
builders to build our applications. There are two main types of Angular
application:
client-side
rendering: the entire application is built
client-side. In other words, the application is represented in DOM (Document
Object Model) format. Navigation, data fetching and templating are managed
client-side instead of server-side.
server-side
rendering: the application is rendered in
HTML format, each page of the application is rendered in HTML format in
response to navigation, and then this page is rehydrated to make it dynamic.
Depending on the type of
application, the builder was different.
Angular used the builder:
1)
@angular-devkit/build-angular:browser
to build a client-side rendering application
2)
@nguniversal/builders:ssr-dev-server
to build a server-side rendering application.
Requesting to subscribe to my YouTube channel! And explore the video content...