| Aspetto | Vue (progressivo) | React (libreria) | Angular (framework) |
|---|---|---|---|
| Filosofia | Incrementale + flessibile | Minimalista, JSX-focused | Batteries included, opinato |
| Curva apprendimento | Bassa | Media | Alta (TypeScript, Decoratori) |
| Struttura | Opzionale | Build custom | Rigorosa, opinionata |
| Linguaggio | JavaScript/TypeScript | JavaScript/JSX | TypeScript obbligatorio |
| State management | Reactive data | useState, Context | RxJS, Signals, Services |
| Dimensione app | Piccola → Media | Piccola → Grande | Media → Molto Grande |
| Community | Crescente | Enorme | Enterprise-focused |
✅ Usa Angular se: - Progetto grande e complesso con struttura rigorosa - Team enterprise che apprezzerebbe uno standard fisso - Necessità di Dependency Injection e architecture patterns chiari - Applicazioni real-time con RxJS (reactive programming)
❌ Evita Angular se: - Prototipo rapido o progetto piccolo - Team preferisce flessibilità e libertà stilistica - Risorse limitate (overhead di apprendimento TypeScript + Angular)
1. Installa Angular CLI globalmente:
2. Crea un nuovo progetto:
3. Avvia il dev server:
Nota (Windows PowerShell): se riscontri errori di policy, esegui:
my-angular-app/
├── src/
│ ├── app/
│ │ ├── app.component.ts # Component root
│ │ ├── app.component.html # Template
│ │ ├── app.component.css # Stili
│ │ └── services/ # Servizi (DI)
│ ├── main.ts # Entry point
│ └── index.html # Template HTML
├── angular.json # Config Angular
├── tsconfig.json # Config TypeScript
├── package.json
└── dist/ # Output build (produzione)
Un componente Angular è composto da 4 elementi:
// product.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-product', // Tag HTML custom
templateUrl: './product.component.html', // File HTML
styleUrls: ['./product.component.css'] // File CSS
})
export class ProductComponent {
// Classe TypeScript: stato e logica
productTitle: string = 'Laptop';
price: number = 999;
getDescription(): string {
return `${this.productTitle} - €${this.price}`;
}
}Generare un componente (consigliato):
Questo crea automaticamente 4 file: - product.component.ts (logica) - product.component.html (template) - product.component.css (stili) - product.component.spec.ts (test)
{ }Renderizza proprietà e espressioni direttamente nel template:
[property]Assegna una proprietà TypeScript a una proprietà DOM:
(event)Ascolta eventi DOM e chiama metodi TypeScript:
<!-- Click event -->
<button (click)="onBuyClick()">Compra</button>
<!-- Event binding con parametro -->
<button (click)="addToCart(productTitle)">Aggiungi: {{ productTitle }}</button>
<!-- Form submit -->
<form (ngSubmit)="handleSubmit()">
<input type="text" [(ngModel)]="name" />
<button type="submit">Invia</button>
</form>Classe TypeScript:
[(ngModel)]Sincronizza dati in entrambe le direzioni (componente ↔︎ form):
*ngIf: rendering condizionale<!-- Mostra se condition è true, altrimenti rimuove dal DOM -->
<div *ngIf="isLoggedIn">
<p>Benvenuto, {{ username }}!</p>
</div>
<div *ngIf="!isLoggedIn">
<p>Per favore, effettua il login.</p>
</div>
<!-- if/else-if/else -->
<div *ngIf="status === 'loading'; else loaded">
<p>Caricamento...</p>
</div>
<ng-template #loaded>
<p>Dati caricati!</p>
</ng-template>*ngFor: iterazione su array<!-- Lista semplice -->
<ul>
<li *ngFor="let item of items">{{ item }}</li>
</ul>
<!-- Con indice e length -->
<div *ngFor="let product of products; let i = index; let len = count">
<p>#{{ i + 1 }} di {{ len }}: {{ product.title }}</p>
</div>
<!-- Filtrare e trasformare -->
<div *ngFor="let p of (products | filter:'expensive')">
{{ p.title }}: €{{ p.price }}
</div>Classe TypeScript:
@Input(): parent → childRicevi dati da un componente genitore:
Utilizzo nel genitore:
@Output() e EventEmitter: child → parentInvia dati al genitore tramite evento:
// product-card.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-product-card',
template: `
<h3>{{ title }}</h3>
<button (click)="onAddToCart()">Aggiungi al carrello</button>
`
})
export class ProductCardComponent {
title: string = 'Laptop';
@Output() addedToCart = new EventEmitter<string>();
onAddToCart(): void {
this.addedToCart.emit(this.title); // Emetti evento
}
}Genitore ascolta l’evento:
DI è un pattern che fornisce dipendenze esterne a una classe, anziché crearle internamente:
1. Genera un servizio:
2. Crea il servizio (con @Injectable):
// product.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Disponibile globalmente (singleton)
})
export class ProductService {
private products = [
{ id: 1, title: 'Laptop', price: 999 },
{ id: 2, title: 'Mouse', price: 25 }
];
getProducts() {
return this.products;
}
getProductById(id: number) {
return this.products.find(p => p.id === id);
}
addProduct(product: any) {
this.products.push(product);
}
}3. Inietta il servizio nel componente:
// products.component.ts
import { Component, OnInit } from '@angular/core';
import { ProductService } from '../services/product.service';
@Component({
selector: 'app-products',
template: `
<ul>
<li *ngFor="let p of products">{{ p.title }} - €{{ p.price }}</li>
</ul>
`
})
export class ProductsComponent implements OnInit {
products: any[] = [];
// Inietta il servizio nel constructor
constructor(private productService: ProductService) {}
ngOnInit(): void {
// Carica i dati dal servizio
this.products = this.productService.getProducts();
}
}Esercizi consigliati
Esercizio 1: componente semplice (5 min) - Crea componente WelcomeComponent - Proprietà: username: string e greeting: string - Template: mostra “{{ greeting }}, {{ username }}!” - Usa property binding su un input disabled
Esercizio 2: property + event binding (7 min) - Crea CounterComponent - Stato: count: number = 0 - 3 pulsanti: “+1”, “-1”, “Reset” - Mostra il valore corrente con interpolazione - Usa (click) per i pulsanti
Esercizio 3: data binding + direttive (8 min) - Crea TodoListComponent - Array di task: { id, title, completed } - Usa *ngFor per renderizzare la lista - Usa *ngIf per mostrare “No tasks” se lista vuota - Usa [(ngModel)] per aggiungere checkbox completed
Esercizio 4: @Input() (5 min) - Crea TaskItemComponent con @Input() task - Mostra task.title e checkbox - Genitore passa dati tramite [task]="myTask"
Ricostruire una Todo App completa utilizzando Angular con: - Componenti per UI strutturata - Data binding per sincronizzazione dati - Direttive per rendering condizionale e liste - Services per logica CRUD condivisa - @Input() / @Output() per comunicazione
Esercizio Todo App - acceptance criteria
Setup iniziale:
1. TodoService (logica condivisa) - Proprietà: todos: Todo[] array iniziale con 3-4 task di esempio - Metodi: getTodos(), addTodo(title), deleteTodo(id), toggleTodo(id) - Interfaccia: export interface Todo { id: number; title: string; completed: boolean; }
2. TodoForm Component (input nuovo task) - Proprietà: newTaskTitle: string = '' - Event binding su submit: (ngSubmit)="onAdd()" - Two-way binding: [(ngModel)]="newTaskTitle" - Inietta TodoService e chiama addTodo() al submit - Resetta l’input dopo aggiunta
3. TodoItem Component (singolo task) - @Input() todo: Todo - @Output() deleted = new EventEmitter<number>() - Template: checkbox [checked]=“todo.completed”, title, bottone delete - Uso di (click) per emit delete
4. TodoList Component (orchestratore) - Inietta TodoService nel constructor - ngOnInit(): carica this.todos = this.todoService.getTodos() - Template: include TodoForm + lista con *ngFor di TodoItem - Ascolta (deleted) da TodoItem e chiama todoService.deleteTodo(id) - Mostra “No tasks” con *ngIf="!todos || todos.length === 0"
5. AppComponent (root) - Importa TodoList - Template: <app-todo-list></app-todo-list> + heading
Acceptance criteria: - App carica senza errori in console (ng serve) - Aggiungere task funziona (input accetta, submit aggiunge, input resetta) - Checkbox toggle: clickare checkbox marca/marca come completato - Delete: bottone elimina il task dalla lista - Task persistenti durante sessione (array in memory) - Input form pulito, layout leggibile - Nessun warning in console (ng serve) - Destructuring props in TodoItem: @Input() todo!: Todo
Bonus (opzionale): - Salva todos in localStorage e ripristina al refresh - Aggiungi campo edit (doppio click per editare) - Filtri: “Tutti”, “Completati”, “Attivi” - Contatore task (totali + completati)
Niccolò Maltoni