Il nuovo volto di Angular�Più semplice 🎯�Più veloce ⚡️�Più potente 🚀
BEGINNER
EXPERT
INTERMEDIATE
Davide Passafaro
davide-passafaro
davidepassafaro
Angular Google Developer Expert
GDG Roma Città Organizer
Angular Roma Organizer
Senior Frontend Engineer @
AngularJS - 2010
Angular 2 - 2016
ebbasta
Z
I punti deboli di Angular
🧠 Complessità e curva di apprendimento ripida
🐢 Prestazioni in app complesse
🔒 Limitata flessibilità rispetto a librerie più leggere
🧩 Verbosità del codice
📉 SEO limitato nelle SPA
🔄 Aggiornamenti frequenti e breaking changes
Angular 3 - 2023
naaah
Standalone Components
AppComponent
@NgModule({
declarations: [
HeaderComponent,
FooterComponent,
HighlightDirective,
TruncatePipe
],
imports: [ CommonModule, FormsModule, ReactiveFormsModule ],
exports: [
HeaderComponent,
FooterComponent,
HighlightDirective,
TruncatePipe
],
providers: [ LoggerService ]
})
export class SharedModule {}
<section class="dashboard" appSectionHighlight>
<h1 appTitleCase>Panoramica Utente</h1>
<div *appShowWhenReady="isDataLoaded">
<p>Benvenuto, {{ userName | appFormatName }}</p>
<ul>
<li
*ngFor="let item of items"
[appSelectableItem]="item === selectedItem"
>
{{ item.label }}
</li>
</ul>
</div>
</section>
@NgModule({
// Il nostro unico componente
declarations: [ItemComponent],
// Moduli base di cui ha bisogno
imports: [CommonModule, ...],
// Lo rendiamo disponibile ad altri moduli
exports: [ItemComponent]
})
export class ItemModule {}
componente + modulo = standalone
@Module
@Component
@Component({
selector: 'app-page',
standalone: true,
imports: [NgIf, TitleCasePipe, CardComponent],
providers: [MyService],
template: `
<h1>{{ title | titlecase }}</h1>
<p *ngIf="showCard">
<app-card></app-card>
</p>
`
})
export class PageComponent {
title = 'ciao a tutti';
showCard = true;
constructor(private service: MyService) {
service.log('PageComponent inizializzato');
}
}
Default con v19
@Component({
selector: 'app-admin-page',
standalone: true,
imports: [DashboardComponent]
})
export class AdminPageComponent {}
export const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent }
];
Come modulo
Come componente
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
@NgModule({
declarations: [
AppComponent,
HomeComponent,
AboutComponent
],
imports: [
BrowserModule,
RouterModule.forRoot(routes)
],
bootstrap: [AppComponent]
})
export class AppModule {}
bootstrapApplication(AppComponent, appConfig)
.catch(err => console.error(err));
const routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
const appConfig = {
providers: [provideRouter(routes)]
};
AppComponent
AppModule
📦
🪦
📚
I moduli ancora sono supportati, ma per casi avanzati
Librerie
Feature design
Compatibilità con codice legacy
I principali vantaggi
Il nuovo control flow
e le nuove istruzioni
<div *ngIf="users.length > 0; else noUsers">
<ul>
<li
*ngFor="let user of users"
[ngClass]="{ 'active-user': user.active }"
[ngStyle]="{ 'font-weight': user.active ? 'bold' : 'normal' }"
>
{{ user.name }} <span *ngIf="user.active">(Attivo)</span>
</li>
</ul>
</div>
<ng-template #noUsers>
<p>Nessun utente disponibile.</p>
</ng-template>
<div *ngIf="isLoggedIn; else loggedOut">
<p>Welcome back, {{ username }}!</p>
</div>
<ng-template #loggedOut>
<p>Please log in.</p>
</ng-template>
@if (isLoggedIn) {
<p>Welcome back, {{ username }}!</p>
} @else if (isGuest) {
<p>Hello Guest, please sign up!</p>
} @else {
<p>Please log in.</p>
}
*ngIf
@if
<ul>
<li *ngFor="let user of users; let i = index; trackBy: trackById">
{{ i + 1 }}. {{ user.name }}
</li>
<li *ngIf="products.length === 0" class="empty">No users found</li>
</ul>
<ul>
@for (user of users; track user.id; let i = $index) {
<li>{{ i + 1 }}. {{ user.name }}</li>
} @empty {
<li>No users found</li>
}
</ul>
*ngFor
@for
<div [ngSwitch]="role">
<p *ngSwitchCase="'admin'">
Admin
</p>
<p *ngSwitchCase="'user'">
User
</p>
<p *ngSwitchDefault>
Guest
</p>
</div>
@switch (role) {
@case ('admin') {
<p>Admin</p>
}
@case ('user') {
<p>User</p>
}
@default {
<p>Guest</p>
}
}
[ngSwitch]
@switch
@Component({
selector: 'app-example',
standalone: true,
imports: [
CommonModule,
NgIf,
NgFor,
NgSwitch,
NgSwitchCase,
NgSwitchDefault
],
template: `...`
})
export class ExampleComponent {}
Migrazione automatica con le schematics
I principali vantaggi
<div [ngClass]="{ admin: isAdmin, dense: density === 'high' }"></div>
<div [ngStyle]="{ color: isActive ? 'green' : 'red', 'font-size.px': fontSize }"></div>
<div [class]="{ admin: isAdmin, dense: density === 'high' }"></div>
<div [style]="{ color: isActive ? 'green' : 'red', 'font-size.px': fontSize }"></div>
[ngClass] e [ngStyle]
[class] e [style]
ng generate @angular/core:ngclass-to-class
ng generate @angular/core:ngstyle-to-style
@Component({
selector: 'app-example',
standalone: true,
imports: [
CommonModule,
NgIf,
NgFor,
NgSwitch,
NgSwitchCase,
NgSwitchDefault,
NgClass,
NgStyle
],
template: `...`
})
export class ExampleComponent {}
I principali vantaggi
Signals, Signal-based APIs e Angular Zoneless
@Component({
selector: "my-component",
template: `
<div>{{ counter }}</div>
<button (click)="increase()"></button>
`,
})
export class MyComponent {
counter = 0;
increase(): void {
this.counter++;
}
}
@Component({
selector: "my-component",
template: `
<div>{{ counter }}</div>
<button (click)="increase()"></button>
`,
})
export class MyComponent {
counter = 0;
increase(): void {
setInterval(() => {
this.counter++;
}, 1000);
}
}
Zone.js
Z
Grazie a
NgZone
AppComponent
ApplicationRef
ApplicationRef
AppComponent
Event
<main>
<h2>Hello {{ firstName }}!</h2>
<input type="text" [(ngModel)]="firstName" />
</main>
<img
[alt]="hero.name"
[src]="hero.imageUrl"
/>
<app-hero-detail
[hero]="hero"
/>
@if(show){
@for (item of list; track item.id) {
<p>{{ item.name }}</p>
<img [src]="item.image" />
}
}
@Component({
selector: 'my-card',
templateUrl: './my-card.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyCardComponent {
...
}
@Component({
selector: 'my-component',
template: `
<button (click)="addItem()">Add</button>
<my-child [items]="items" />
`
})
export class MyComponent {
items = [];
addItem() {
this.items.push(42);
this.items = [...this.items, 42];
}
}
@Component({
selector: 'my-component',
template: `
<button (click)="loadData()">Load</button>
<my-child [items]="items" />
`
})
export class MyComponent {
cdr = inject(ChangeDetectorRef);
myService = inject(MyService);
items = [];
loadData() {
this.myService.getData().subscribe(data => {
this.items = data;
this.cdr.markForCheck();
})
}
}
Default
OnPush
Controlla tutti i valori associati ai componenti�ad ogni evento
Scatena la CD in caso di
Performance
DevEx
// Signal
const year = signal(2025);
const confName = signal('DevFest Modena');
year.set(2026);
year.update(year => year + 1);
// Computed
const message = computed(() => `${confName()} ${year()}`);
// Effect
effect(() => {
console.log(`Ciao ${message()}`);
});
// Signal
const year = signal(2025);
const confName = signal('DevFest Modena');
year.set(2026);
year.update(year => year + 1);
// Computed
const message = computed(() => `${confName()} ${year()}`);
// Effect
effect(() => {
console.log(`Ciao ${message()}`);
});
// Signal
const year = signal(2025);
const confName = signal('DevFest Modena');
year.set(2026);
year.update(year => year + 1);
// Computed
const message = computed(() => `${confName()} ${year()}`);
// Effect
effect(() => {
console.log(`Ciao ${message()}`);
});
@Component({
selector: 'app-example',
template: `
<p>Benvenuti al {{ message() }}</p>
<button (click)="incrementYear()"> +1 </button>
`,
})
export class ExampleComponent {
year = signal(2025);
confName = signal('DevFest Modena');
message = computed(() => `${this.confName()} ${this.year()}`);
constructor() {
effect(() => console.log(`Ciao ${this.message()}`));
}
incrementYear() {
this.year.update(y => y + 1);
}
}
I principali vantaggi
Signal-based APIs
@Component({
selector: 'app-counter',
template: `
<h3>Contatore: {{ count }}</h3>
<button (click)="decrement()">-</button>
<button (click)="increment()">+</button>
`
})
export class CounterComponent {
@Input() count = 0;
@Output() countChange = new EventEmitter<number>();
increment() {
this.countChange.emit(this.count + 1);
}
decrement() {
this.countChange.emit(this.count - 1);
}
}
@Component({
selector: 'app-counter',
template: `
<h3>Contatore: {{ count }}</h3>
<button (click)="decrement()">-</button>
<button (click)="increment()">+</button>
`
})
export class CounterComponent {
count = input<number>();
countChange = output<number>();
increment() {
this.countChange.emit(this.count() + 1);
}
decrement() {
this.countChange.emit(this.count() - 1);
}
}
input( ) e output( )
@Input e @Output
@Component({
selector: 'my-component',
template: `
<h2>{{ firstName }}</h2>
<p>Age: {{ age }}</p>
<p *ngIf="isAdult">✅ Adult</p>
<p *ngIf="!isAdult">❌ Minor</p>
`
})
export class MyComponent implements OnChanges {
@Input() firstName: string = '';
@Input() age: number = 0;
isAdult: boolean = false;
ngOnChanges(): void {
this.isAdult = this.age >= 18;
}
}
@Component({
selector: 'my-component',
template: `
<h2>{{ firstName() }}</h2>
<p>Age: {{ age() }}</p>
<p *ngIf="isAdult()">✅ Adult</p>
<p *ngIf="!isAdult()">❌ Minor</p>
`
})
export class MyComponent {
firstName = input<string>('');
age = input<number>(0);
isAdult = computed(() => this.age() >= 18);
}
computed( ) e effect( )
ngOnChanges( )
@Component({
selector: 'app-counter',
template: `
<h3>Contatore: {{ count }}</h3>
<button (click)="decrement()">-</button>
<button (click)="increment()">+</button>
`
})
export class CounterComponent {
@Input() count = 0;
@Output() countChange = new EventEmitter<number>();
increment() {
this.countChange.emit(this.count + 1);
}
decrement() {
this.countChange.emit(this.count - 1);
}
}
@Component({
selector: 'app-counter',
template: `
<h3>Contatore: {{ count }}</h3>
<button (click)="decrement()">-</button>
<button (click)="increment()">+</button>
`
})
export class CounterComponent {
count = model<number>();
increment() {
this.count.set(this.count() + 1);
}
decrement() {
this.count.set(this.count() - 1);
}
}
model( )
@Input e @Output
@ViewChild('input') input!: ElementRef;
@ViewChildren(ItemComponent) items!: QueryList<ItemComponent>;
@ContentChild('input') input!: ElementRef;
@ContentChildren(ItemComponent) items!: QueryList<ItemComponent>;
input = viewChild<ElementRef>('input');
items = viewChildren(ItemComponent);
input = contentChild<ElementRef>('input');
items = contentChildren(ItemComponent);
@ViewChild e @ViewChildren, @ContentChild e @ContentChildren
viewChild( ) e viewChildren( ), contentChild( ) e contentChildren( )
I principali vantaggi
BUT
Zone.js
Z
Zone on, Zone off
Zoneless è finalmente stabile 🥳
ng new app��? Do you want to create a 'zoneless' application without zone.js? (y/N)
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection()]
});
npm uninstall zone.js
WRAP UP
Angular continua ad evolvere da 15 anni
AngularJS�2010
Angular�2016
Angular�2023
Signals e�Zoneless
Standalone Components
Cosa abbiamo visto oggi
Nuovo�Control Flow
Il nuovo Angular ha anche tanto altro
I punti deboli di Angular
🧠 Complessità e curva di apprendimento ripida
🐢 Prestazioni in app complesse
🔒 Limitata flessibilità rispetto a librerie più leggere
🧩 Verbosità del codice
📉 SEO limitato nelle SPA
🔄 Aggiornamenti frequenti e breaking changes
Talk a parte
Z
Il nuovo volto di Angular
🎯 Più semplice con gli standalone component
⚡️ Più veloce con le nuove istruzioni
🚀 Più potente grazie ai Signal e a Zoneless
Grazie
Davide Passafaro
davide-passafaro
davidepassafaro
Angular Google Developer Expert
GDG Roma Città Organizer
Angular Roma Organizer
Senior Frontend Engineer @