?? Discover What's New in Angular 17!

?? Discover What's New in Angular 17!

Angular has undergone a transformation, boasting exciting updates, including revamped documentation and a fresh logo, all aimed at providing developers with a new home at angular.dev.

??Built-in control flow

Under the hood, the Angular compiler transforms the syntax to efficient JavaScript instructions that could perform control flow, lazy loading, and more.

We used the new block syntax for an optimized, built-in control flow. After running user studies we identified that a lot of developers struggle with *ngIf, *ngSwitch, and *ngFor. Working with Angular since 2016 and being part of the Angular team for the past 5 years, I personally still have to look up the syntax of *ngFor and trackBy.

?Conditional statements;-

Let’s look at a side by side comparison with *ngIf:

<div *ngIf="loggedIn; else anonymousUser">
  The user is logged in
</div>
<ng-template #anonymousUser>
  The user is not logged in
</ng-template>        

With the built-in if statement, this condition will look like:

@if (loggedIn) {
  The user is logged in
} @else {
  The user is not logged in
}        

The improved ergonomics is even more visible with *ngSwitch:

<div [ngSwitch]="accessLevel">
  <admin-dashboard *ngSwitchCase="admin"/>
  <moderator-dashboard *ngSwitchCase="moderator"/>
  <user-dashboard *ngSwitchDefault/>
</div>        

which with the built-in control flow turns into:

@switch (accessLevel) {
  @case ('admin') { <admin-dashboard/> }
  @case ('moderator') { <moderator-dashboard/> }
  @default { <user-dashboard/> }
}        

The new control flow enables significantly better type-narrowing in the individual branches in @switch which is not possible in *ngSwitch.

??Built-in for loop

One of my most favorite updates is the built-in for loop that we introduced, which on top of the developer experience improvements pushes Angular’s rendering speed to another level!

Its basic syntax is:

@for (user of users; track user.id) {
  {{ user.name }}
} @empty {
  Empty list of users
}        

We often see performance problems in apps due to the lack of trackBy function in *ngFor. A few differences in @for are that track is mandatory to ensure fast diffing performance. In addition, it’s way easier to use since it’s just an expression rather than a method in the component’s class. The built-in @for loop also has a shortcut for collections with zero items via an optional @empty block.

??Deferrable views

Deferrable views can be used in component template to defer the loading of select dependencies within that template. Those dependencies include components, directives, and pipes, and any associated CSS. To use this feature, you can declaratively wrap a section of your template in a @defer block which specifies the loading conditions.

Deferrable views

?? In order for dependencies within a @defer block to be deferred, they need to meet two conditions:

  1. They must be standalone. Non-standalone dependencies cannot be deferred and will still be eagerly loaded, even inside of @defer blocks.
  2. They must not be directly referenced from the same file, outside of @defer blocks; this includes ViewChild queries.

Transitive dependencies of the components, directives, and pipes used in the defer block can be standalone or NgModule based and will still be deferred.

?? 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 {
  <large-component />
} @loading (after 100ms; minimum 1s) {
  <img alt="loading..." src="loading.gif" />
}        

In the example above, Angular first renders the contents of the placeholder block. When it becomes visible in the viewport, the loading of the <large-component /> component starts. Once the loading is completed, Angular removes the placeholder and renders the component.

There are also blocks for loading and error states:

@defer {
  <calendar-cmp />
} @error {
  <p>Failed to load the calendar</p>
}        
defer (on viewport) {
  <comment-list/>
} @loading {
  Loading…
} @error {
  Loading failed :(
} @placeholder {
  <img src="comments-placeholder.png">
}        

That’s it! There’s a ton of complexity under the hood that Angular manages for you.

?? Deferrable views offer a few more triggers:

  • on idle — lazily load the block when the browser is not doing any heavy lifting
  • on immediate — start lazily loading automatically, without blocking the browser
  • on timer(<time>) — delay loading with a timer
  • 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
  • on interaction and on interaction(<ref>) — enables you to initiate lazy loading when the user interacts with a particular element
  • on hover and on hover(<ref>) — triggers lazy loading when the user hovers an element
  • when <expr> — enables you to specify your own condition via a boolean expression

?? Angular Signals

A signal is a wrapper around a value that notifies interested consumers when that value changes. Signals can contain any value, from simple primitives to complex data structures.

You read a signal's value by calling its getter function, which allows Angular to track where the signal is used.

Signals may be either writable or read-only.

Writable signals

Writable signals provide an API for updating their values directly. You create writable signals by calling the signal function with the signal's initial value:

const count = signal(0);
// Signals are getter functions - calling them reads their value.
console.log('The count is: ' + count());

count.set(3);

// Increment the count by 1.
count.update(value => value + 1);        

To change the value of a writable signal, either .set() it directly:

or use the .update() operation to compute a new value from the previous one:

Computed signals

Computed signal are read-only signals that derive their value from other signals. You define computed signals using the computed function and specifying a derivation:

const count: WritableSignal<number> = signal(0);
const doubleCount: Signal<number> = computed(() => count() * 2);        

The doubleCount signal depends on the count signal. Whenever count updates, Angular knows that doubleCount needs to update as well.


要查看或添加评论,请登录

Shivam Upadhyay的更多文章

社区洞察

其他会员也浏览了