What is coming in Angular v17?

Agenda

  • Signal 🚦 (stable)
  • New Control flow 🎮
  • Deferrable views ⌛️
  • Improved support for server-side rendering

  • Automatic CSS Removal on component destroy
  • Opt-in Support for View Transitions API
  • And more...

@pankajparkar

www.pankajparkar.com

Pankaj P. Parkar

Principal Application Devloper

  • Ex- Microsoft MVP (2015-22)

  • Angular GDE

About Me!

pankajparkar

Signal🚦

  • helps to update application state in optimised way
  • based on reactive principals

pankajparkar

Signal🚦

FirstName LastName FullName
Andrew Robertson Pankaj Parkar
John Doe John Doe
firstName = 'Andy';

lastName = 'Robertson';

fullName = `${this.firstName} ${this.lastName}`;

updateFirstName() {
  this.firstName = 'Andrew';
}

this.updateFirstName();
console.log(this.fullName);
get fullName() {
  return `${this.firstName} ${this.lastName}`;
}

pankajparkar

What is Signal?

const count = signal(0);

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

// Set value directly
count.set(3);

// if want to update value based on last value
count.update(value => value + 1);

// Check count value is 
console.log('The count is: ' + count()); // 3

pankajparkar

Signal Primitives

signal(value, configuration) - Define

  • Writable / NonWritable

computed - derived value

  • Used for deriving a value from other signals

effect - react to change

  • React to changes in state in callback function

pankajparkar

Signal implementation

@Component({
  ...
})
export class SignalComponent {
  firstName = signal('Pankaj')
  lastName = signal('Parkar')
  fullName = computed(() => 
    `${this.firstName()} ${this.lastName()}`
  );

  updateFirstName() {
    this.firstName.set('Pankaj 1');
  }
}
<table>
  <tr>
    <th>First Name</th>
    <th>Last Name</th>
    <th>Full Name</th>
  </tr>
  <tr>
    <td>{{firstName()}}</td>
    <td>{{lastName()}}</td>
    <td>{{fullName()}}</td>
  </tr>
</table>

<button mat-button (click)="updateFirstName()">
  Update FirstName
</button>

pankajparkar

New Control flow 🎮

<h3>if else</h3>
<div>
  <input #checkbox type="checkbox" [checked]="isChecked()"
    (change)="isChecked.set(checkbox.checked)"/>
</div>
<div>
@if (isChecked()) {
  <span>It is checked</span>
} 
@else {
  <span>Not checked</span>
}
</div>
@if (user.isHuman) {
  <human-profile [data]="user" />
} @else if (user.isRobot) {
    <robot-profile [data]="user" />
  }
} @else {
  <p>The profile is unknown!
}

pankajparkar

New Control flow 🎮

<ul>
  @for (item of collection; track item.id; let index = $index) {
    <li>
      <strong>{{item.name}}</strong> index={{index}}
    </li>
  }
  @empty {
    <li>
        No data found
    </li>
  }
</ul>

pankajparkar

<ul>
  <li *ngFor="item of collection; trackBy: trackByFn; let index = $index">
    <strong>{{item.name}}</strong> index={{index}}
  </li>
  <li *ngIf="collection.length === 0">
      No data found
  </li>
</ul>

New Control flow 🎮

@switch (selectedValue()) {
  @case (1) {
    <span>Case 1</span>
  }
  @case (2) {
    <span>Case 2</span>
  }
  @case (3) {
    <span>Case 3</span>
  }
  @case (4) {
    <span>Case 4</span>
  }
  @default {
    <span>Default case null and 4</span>
  }
}

pankajparkar

Deferrable views

 @defer {
    <app-child />
 }
 @defer (on idle) {
    <app-child />
 }
<div>
  @defer(on viewport(container), idle) {
    <app-child />
  }
</div>
function defer_for_block {
  return [
    () => import('./app-child.component')
  ];
}

pankajparkar

Deferrable views

@if (user.isHuman) {
  <human-profile [data]="user" />
} @else if (user.isRobot) {
  <!-- robot users are rare, so load their profiles lazily -->
  @defer {
    <robot-profile [data]="user" />
  }
} @else {
  <p>The profile is unknown!
}

pankajparkar

Deferrable views

@defer (when isVisible() && foo; 
  on hover(button), timer(10s), idle, immediate, interaction(button);
  prefetch on immediate; prefetch when isDataLoaded()) {
    <calendar-cmp [date]="current"/>
} 
@placeholder (minimum 500){
  Placeholder content!
}
@loading (minimum 1s; after 100ms){
  Loading...
}
@error {
  Loading failed :( 
}

pankajparkar

Deferrable views - triggers

pankajparkar

Automatic CSS Removal on component destroy

CSS will be remove once component is destroyed

pankajparkar

View Transitions API

pankajparkar

Opt-in Support for View Transitions API

navigate() {
  this.ngZone.runOutsideAngular(() => {
    if(!this.document.startViewTransition){
      this.ngZone.run(() => {
        void this.router.navigate(['detail', '42']);
      });
    }
  });
}

pankajparkar

Opt-in Support for View Transitions API

import { provideRouter, withViewTransitions } from '@angular/router';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(
      [...], // Your route configurations
      withViewTransitions() // Enable View Transitions
    ),
  ],
})

pankajparkar

More features

  • Improved support for server-side rendering
  • Router Properties Moved to provideRouter and RouterModule.forRoot
  • Errors When Page Render is Slowed Down by Lazy Loaded Images
  • New ways to setup route for testing

pankajparkar

Pankaj P. Parkar

Sr. Technology Consultant, Virtusa

  • Ex- Microsoft MVP (2015-22)

  • Angular GDE

  • Stackoverflow Topuser

About Me!

pankajparkar

@pankajparkar

www.pankajparkar.com

Q & A

@pankajparkar

  • https://www.angularaddicts.com/p/angular-17-new-control-flow-with-signals
  • https://netbasal.com/angular-v17s-view-transitions-navigate-in-elegance-f2d48fd8ceda
  • https://netbasal.com/angular-v17s-view-transitions-navigate-in-elegance-f2d48fd8ceda
  • https://konstantin-denerz.com/view-transitions-with-angular-spa/

References

@pankajparkar

Made with Slides.com