Angular per Front-End Design
Tutorial pratico completo per creare applicazioni moderne con Angular. Dalla configurazione iniziale fino al deployment, con focus su design contemporaneo e user experience.
Landing & Cards
Crea la tua prima landing page con componenti riutilizzabili
Routing & Layout
Implementa navigazione fluida e struttura modulare
Galleria & Filtri
Costruisci gallerie interattive con filtri dinamici
Form & API
Integra form avanzati con mock API
Requisiti e Setup
Prima di iniziare, assicurati di avere tutti gli strumenti necessari per sviluppare con Angular.
Strumenti Essenziali
- Node.js LTS (v18.x o v20.x) con npm incluso
- Angular CLI per la gestione dei progetti
- Editor moderno come VS Code
- Connessione internet per installare dipendenze
Verifica Installazione
node -v
npm -v
Installa Angular CLI
npm install -g @angular/cli
Avvia il Primo Progetto
ng new nome-progetto --routing --style=scss
cd nome-progetto
ng serve --open
Opzioni di Hosting
- Sviluppo: ng serve (localhost:4200)
- Build: ng build --prod (genera cartella dist/)
- Deploy: GitHub Pages, Netlify, Vercel, Firebase Hosting
Capitolo 1 – Prima Landing con Card Design
Creiamo subito una landing page visualmente accattivante con una griglia di card per mostrare progetti di design.
Setup Iniziale
ng new portfolio-design --routing --style=scss
cd portfolio-design
ng serve --open
Genera i Componenti
ng generate component header
ng generate component home
ng generate component project-card
app.component.html
<app-header></app-header>
<router-outlet></router-outlet>
header.component.html
<nav class="navbar">
<div class="nav-brand">F.E. Design</div>
<ul class="nav-menu">
<li><a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a></li>
<li><a routerLink="/designs" routerLinkActive="active">Designs</a></li>
<li><a routerLink="/portfolio" routerLinkActive="active">Portfolio</a></li>
</ul>
</nav>
home.component.html
<section class="hero-section">
<h1>Design Portfolio</h1>
<p>Scopri i nostri progetti e creazioni più innovative</p>
</section>
<section class="projects-grid">
<app-project-card
*ngFor="let item of projects"
[title]="item.title"
[image]="item.image"
[tags]="item.tags"
[description]="item.description">
</app-project-card>
</section>
project-card.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-project-card',
template: `
<div class="project-card">
<div class="card-image">
<img [src]="image" [alt]="title" loading="lazy">
<div class="card-overlay">
<button class="view-btn">Visualizza</button>
</div>
</div>
<div class="card-content">
<h3>{{ title }}</h3>
<p>{{ description }}</p>
<div class="tags">
<span class="tag" *ngFor="let tag of tags">{{ tag }}</span>
</div>
</div>
</div>
`,
styleUrls: ['./project-card.component.scss']
})
export class ProjectCardComponent {
@Input() title!: string;
@Input() image!: string;
@Input() tags: string[] = [];
@Input() description!: string;
}
Capitolo 2 – Routing e Layout Avanzato
Implementiamo un sistema di routing robusto per creare un'esperienza di navigazione fluida e moderna.
Configurazione Routing
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { DesignsComponent } from './designs/designs.component';
import { PortfolioComponent } from './portfolio/portfolio.component';
import { ContactComponent } from './contact/contact.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'designs', component: DesignsComponent },
{ path: 'portfolio', component: PortfolioComponent },
{ path: 'contact', component: ContactComponent },
{ path: '**', redirectTo: '' } // Wildcard route
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled'
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
Features Avanzate del Routing
- Route Guards: Proteggi rotte sensibili
- Lazy Loading: Carica moduli solo quando necessario
- Route Parameters: Passa dati tra componenti
- Query Params: Gestisci filtri e stato dell'app
designs.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-designs',
templateUrl: './designs.component.html',
styleUrls: ['./designs.component.scss']
})
export class DesignsComponent implements OnInit {
designs = [
{
id: 1,
title: 'UI Design System',
image: 'https://picsum.photos/seed/ui1/600/400',
tags: ['UI', 'Design System', 'Figma'],
description: 'Sistema completo di componenti UI modulari'
},
{
id: 2,
title: 'Mobile App Interface',
image: 'https://picsum.photos/seed/mobile1/600/400',
tags: ['Mobile', 'UX', 'Prototyping'],
description: 'Interfaccia intuitiva per app mobile'
}
];
ngOnInit(): void {
// Animazioni di entrata
this.animateCards();
}
private animateCards(): void {
// Implementa animazioni staggered
}
}
Capitolo 3 – Galleria Interattiva e Filtri
Creiamo una galleria dinamica con filtri avanzati e micro-interazioni che migliorano l'esperienza utente.
Funzionalità Implementate
- Filtri dinamici per categoria
- Ricerca live nei progetti
- Animazioni fluide per transizioni
- Layout responsive con CSS Grid
gallery-filter.component.ts
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
@Component({
selector: 'app-gallery-filter',
template: `
<div class="filter-controls">
<div class="search-box">
<input [formControl]="searchControl"
placeholder="Cerca progetti..."
class="search-input">
</div>
<div class="filter-buttons">
<button
*ngFor="let filter of filters"
[class.active]="activeFilter === filter.value"
(click)="setFilter(filter.value)"
class="filter-btn">
{{ filter.label }}
</button>
</div>
</div>
<div class="gallery-grid">
<div *ngFor="let project of filteredProjects; trackBy: trackByProjectId"
class="gallery-item"
[@slideIn]>
<app-project-card
[title]="project.title"
[image]="project.image"
[tags]="project.tags">
</app-project-card>
</div>
</div>
`,
animations: [
// Definisci animazioni personalizzate
]
})
export class GalleryFilterComponent implements OnInit {
searchControl = new FormControl('');
activeFilter = 'all';
filters = [
{ label: 'Tutti', value: 'all' },
{ label: 'UI Design', value: 'ui' },
{ label: 'Branding', value: 'branding' },
{ label: 'UX Research', value: 'ux' }
];
projects = [
{
id: 1,
title: 'Dashboard Analytics',
image: 'https://picsum.photos/seed/dash1/600/400',
tags: ['ui', 'dashboard'],
category: 'ui'
},
{
id: 2,
title: 'Brand Identity',
image: 'https://picsum.photos/seed/brand1/600/400',
tags: ['branding', 'logo'],
category: 'branding'
}
];
filteredProjects = [...this.projects];
ngOnInit(): void {
// Implementa ricerca live
this.searchControl.valueChanges
.pipe(debounceTime(300), distinctUntilChanged())
.subscribe(searchTerm => {
this.filterProjects(searchTerm || '');
});
}
setFilter(filter: string): void {
this.activeFilter = filter;
this.filterProjects(this.searchControl.value || '');
}
private filterProjects(searchTerm: string): void {
let filtered = [...this.projects];
// Filtra per categoria
if (this.activeFilter !== 'all') {
filtered = filtered.filter(p => p.category === this.activeFilter);
}
// Filtra per termine di ricerca
if (searchTerm) {
filtered = filtered.filter(p =>
p.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
p.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()))
);
}
this.filteredProjects = filtered;
}
trackByProjectId(index: number, project: any): number {
return project.id;
}
}
Capitolo 4 – Form Avanzati e Mock API
Implementiamo form reattivi con validazione avanzata e integrazione con API mock per simulare interazioni real-world.
Setup Moduli
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
// componenti...
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
HttpClientModule,
BrowserAnimationsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
contact-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ContactService } from '../services/contact.service';
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'app-contact-form',
template: `
<div class="contact-container">
<div class="contact-header">
<h2>Inizia il tuo progetto</h2>
<p>Raccontaci la tua idea e trasformiamola in realtà</p>
</div>
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()" class="contact-form">
<div class="form-grid">
<div class="form-field">
<label for="name">Nome *</label>
<input
id="name"
formControlName="name"
placeholder="Il tuo nome"
[class.error]="isFieldInvalid('name')">
<div *ngIf="isFieldInvalid('name')" class="error-message">
Nome è obbligatorio
</div>
</div>
<div class="form-field">
<label for="email">Email *</label>
<input
id="email"
type="email"
formControlName="email"
placeholder="[email protected]"
[class.error]="isFieldInvalid('email')">
<div *ngIf="isFieldInvalid('email')" class="error-message">
Inserisci un'email valida
</div>
</div>
<div class="form-field">
<label for="company">Azienda</label>
<input
id="company"
formControlName="company"
placeholder="Nome azienda (opzionale)">
</div>
<div class="form-field">
<label for="budget">Budget</label>
<select id="budget" formControlName="budget">
<option value="">Seleziona budget</option>
<option value="5k-10k">€5.000 - €10.000</option>
<option value="10k-25k">€10.000 - €25.000</option>
<option value="25k+">€25.000+</option>
</select>
</div>
</div>
<div class="form-field full-width">
<label for="message">Messaggio *</label>
<textarea
id="message"
formControlName="message"
rows="5"
placeholder="Descrivi il tuo progetto..."
[class.error]="isFieldInvalid('message')"></textarea>
<div *ngIf="isFieldInvalid('message')" class="error-message">
Messaggio è obbligatorio
</div>
</div>
<div class="form-actions">
<button
type="submit"
class="submit-btn"
[disabled]="contactForm.invalid || isSubmitting"
[@buttonState]="isSubmitting ? 'loading' : 'idle'">
<span *ngIf="!isSubmitting">Invia Richiesta</span>
<span *ngIf="isSubmitting">Invio...</span>
</button>
</div>
<div *ngIf="submitMessage"
class="submit-message"
[class.success]="submitSuccess"
[class.error]="!submitSuccess"
[@fadeIn]>
{{ submitMessage }}
</div>
</form>
</div>
`,
animations: [
trigger('buttonState', [
state('idle', style({ transform: 'scale(1)' })),
state('loading', style({ transform: 'scale(0.95)' })),
transition('idle <=> loading', animate('200ms ease'))
]),
trigger('fadeIn', [
transition(':enter', [
style({ opacity: 0, transform: 'translateY(20px)' }),
animate('300ms ease', style({ opacity: 1, transform: 'translateY(0)' }))
])
])
]
})
export class ContactFormComponent implements OnInit {
contactForm!: FormGroup;
isSubmitting = false;
submitMessage = '';
submitSuccess = false;
constructor(
private fb: FormBuilder,
private contactService: ContactService
) {}
ngOnInit(): void {
this.initForm();
}
private initForm(): void {
this.contactForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
company: [''],
budget: [''],
message: ['', [Validators.required, Validators.minLength(10)]]
});
}
isFieldInvalid(fieldName: string): boolean {
const field = this.contactForm.get(fieldName);
return !!(field && field.invalid && (field.dirty || field.touched));
}
async onSubmit(): Promise<void> {
if (this.contactForm.valid) {
this.isSubmitting = true;
this.submitMessage = '';
try {
const formData = this.contactForm.value;
await this.contactService.submitInquiry(formData).toPromise();
this.submitSuccess = true;
this.submitMessage = '✅ Grazie! Il tuo messaggio è stato inviato con successo.';
this.contactForm.reset();
} catch (error) {
this.submitSuccess = false;
this.submitMessage = '❌ Errore durante l\'invio. Riprova più tardi.';
} finally {
this.isSubmitting = false;
}
} else {
this.markAllFieldsAsTouched();
}
}
private markAllFieldsAsTouched(): void {
Object.keys(this.contactForm.controls).forEach(key => {
this.contactForm.get(key)?.markAsTouched();
});
}
}
contact.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { delay, catchError } from 'rxjs/operators';
export interface ContactForm {
name: string;
email: string;
company?: string;
budget?: string;
message: string;
}
@Injectable({
providedIn: 'root'
})
export class ContactService {
private apiUrl = 'http://localhost:3000/api/contacts';
constructor(private http: HttpClient) {}
submitInquiry(formData: ContactForm): Observable<any> {
const headers = new HttpHeaders({
'Content-Type': 'application/json'
});
// Simula chiamata API reale
return this.mockApiCall(formData);
// Per API reale, decommenta questa riga:
// return this.http.post(this.apiUrl, formData, { headers });
}
private mockApiCall(formData: ContactForm): Observable<any> {
// Simula ritardo di rete
const mockResponse = {
success: true,
message: 'Messaggio inviato con successo',
id: Date.now(),
data: formData
};
// Simula errore casuale 10% delle volte
const shouldFail = Math.random() < 0.1;
if (shouldFail) {
return of(null).pipe(
delay(1500),
catchError(() => {
throw new Error('Errore simulato');
})
);
}
return of(mockResponse).pipe(delay(1500));
}
// Metodo per recuperare i contatti (admin)
getContacts(): Observable<ContactForm[]> {
return this.http.get<ContactForm[]>(this.apiUrl);
}
}
Setup JSON Server (Opzionale)
Per testare con una API reale in locale:
# Installa JSON Server globalmente
npm install -g json-server
# Crea db.json nella root del progetto
{
"contacts": [],
"projects": [
{
"id": 1,
"title": "Design System",
"category": "ui",
"image": "https://picsum.photos/seed/ds1/600/400"
}
]
}
# Avvia il server
json-server --watch db.json --port 3000
Risorse e Best Practices
Tutto quello che ti serve per portare il tuo progetto Angular al livello successivo.
🎯 Checklist Pre-Deploy
- Build Production:
ng build --prod - Test Routing: Verifica tutte le rotte
- Ottimizzazione Immagini: Comprimi le immagini
- SEO: Meta tags e Open Graph
- PWA: Service Workers e Manifest
Hosting Platforms
Netlify
Drag & drop deploy, automatico da Git
Vercel
Ottimizzato per framework frontend
Firebase
Google Cloud con database integrato
GitHub Pages
Gratuito per progetti open source
Comandi Essenziali
# Sviluppo
ng serve --open # Avvia dev server
ng generate component nome # Crea componente
ng generate service nome # Crea servizio
ng test # Esegui test
# Build e Deploy
ng build --prod # Build per produzione
ng add @angular/pwa # Aggiungi PWA support
ng update # Aggiorna dipendenze
# Analisi Bundle
ng build --prod --stats-json
npx webpack-bundle-analyzer dist/app-name/stats.json
Performance Tips
- Lazy Loading: Carica moduli on-demand
- OnPush Strategy: Ottimizza change detection
- Track By Functions: Migliora performance delle liste
- Preloading: Pre-carica moduli critici
- Tree Shaking: Rimuovi codice non utilizzato
Risorse Utili
- Angular Docs: angular.io
- Angular Material: Componenti UI pronti
- NgRx: State management per app complesse
- Angular DevTools: Extension browser per debug
- Storybook: Sviluppa componenti in isolamento