Dalla chat AI ai form intelligenti: esempi pratici e codice pronto all'uso per il tuo prossimo progetto

Angular nel 2025: come integrare l'AI nei tuoi componenti e servizi
Dalla chat AI ai form intelligenti: esempi pratici e codice pronto all'uso per il tuo prossimo progetto
L'intelligenza artificiale sta rivoluzionando il modo in cui sviluppiamo applicazioni web. Nel 2025, integrare funzionalità AI in Angular non è più un lusso, ma una necessità per creare esperienze utente innovative e competitive. In questo articolo, esploreremo approcci pratici e moderni per incorporare l'AI nei tuoi progetti Angular.
Prima di immergerci nel codice, consideriamo alcuni casi d'uso concreti:
Nel 2025, l'approccio più pragmatico è utilizzare API AI esistenti piuttosto che implementare modelli in-browser. Questo garantisce prestazioni ottimali e accesso a modelli all'avanguardia.
Iniziamo creando un servizio Angular che gestisca le chiamate AI. Utilizzeremo l'HttpClient di Angular e il nuovo sistema di dependency injection standalone.
// ai.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, catchError, map, throwError } from 'rxjs';
import { environment } from '../environments/environment';
export interface AIMessage {
role: 'user' | 'assistant' | 'system';
content: string;
}
export interface AIResponse {
message: string;
tokens: number;
model: string;
}
@Injectable({
providedIn: 'root'
})
export class AIService {
private http = inject(HttpClient);
private apiUrl = environment.aiApiUrl;
private apiKey = environment.aiApiKey;
generateCompletion(
messages: AIMessage[],
options?: { temperature?: number; maxTokens?: number }
): Observable {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
});
const body = {
model: 'gpt-4',
messages,
temperature: options?.temperature ?? 0.7,
max_tokens: options?.maxTokens ?? 1000
};
return this.http.post(this.apiUrl, body, { headers }).pipe(
map(response => ({
message: response.choices[0].message.content,
tokens: response.usage.total_tokens,
model: response.model
})),
catchError(this.handleError)
);
}
private handleError(error: any): Observable {
console.error('AI Service Error:', error);
return throwError(() => new Error('Errore nella chiamata AI'));
}
}
Creiamo un componente di chat intelligente utilizzando Signals, la nuova API reattiva di Angular.Ecco il codice Angular corrispondente alla demo sopra:
// chat-assistant.component.ts
import { Component, signal, effect, inject, ElementRef, viewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AIService, AIMessage } from './services/ai.service';
@Component({
selector: 'app-chat-assistant',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './chat-assistant.component.html',
styleUrls: ['./chat-assistant.component.scss']
})
export class ChatAssistantComponent {
private aiService = inject(AIService);
// Signals per gestione stato
messages = signal<AIMessage[]>([
{ role: 'assistant', content: 'Ciao! Come posso aiutarti oggi?' }
]);
userInput = signal('');
isLoading = signal(false);
// ViewChild per auto-scroll
private messagesEnd = viewChild('messagesEnd');
constructor() {
// Effect per auto-scroll
effect(() => {
if (this.messages().length > 0) {
this.scrollToBottom();
}
});
}
async sendMessage() {
const message = this.userInput().trim();
if (!message || this.isLoading()) return;
// Aggiungi messaggio utente
this.messages.update(msgs => [...msgs, { role: 'user', content: message }]);
this.userInput.set('');
this.isLoading.set(true);
try {
const response = await this.aiService.generateCompletion(
this.messages()
).toPromise();
this.messages.update(msgs => [
...msgs,
{ role: 'assistant', content: response.message }
]);
} catch (error) {
console.error('Errore:', error);
this.messages.update(msgs => [
...msgs,
{ role: 'assistant', content: 'Mi dispiace, si è verificato un errore.' }
]);
} finally {
this.isLoading.set(false);
}
}
private scrollToBottom() {
setTimeout(() => {
this.messagesEnd()?.nativeElement.scrollIntoView({ behavior: 'smooth' });
}, 100);
}
}
Un altro scenario comune è utilizzare l'AI per assistere gli utenti nella compilazione di form complessi.
// smart-form.component.ts
import { Component, signal, computed, inject } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { AIService } from './services/ai.service';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'app-smart-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<div class="smart-form-container">
<h2>Creazione Prodotto Assistita da AI</h2>
<form [formGroup]="productForm">
<div class="form-group">
<label>Nome Prodotto</label>
<input formControlName="name" type="text" />
</div>
<div class="form-group">
<label>Categoria</label>
<input formControlName="category" type="text" />
</div>
<div class="form-group">
<label>Descrizione</label>
<textarea formControlName="description" rows="4"></textarea>
@if (aiSuggestion()) {
<div class="ai-suggestion">
<strong>💡 Suggerimento AI:</strong>
<p>{{ aiSuggestion() }}</p>
<button type="button" (click)="applySuggestion()">
Usa questo testo
</button>
</div>
}
</div>
<button type="button" (click)="generateDescription()">
✨ Genera descrizione con AI
</button>
</form>
</div>
`
})
export class SmartFormComponent {
private fb = inject(FormBuilder);
private aiService = inject(AIService);
productForm: FormGroup;
aiSuggestion = signal('');
isGenerating = signal(false);
constructor() {
this.productForm = this.fb.group({
name: ['', Validators.required],
category: ['', Validators.required],
description: ['']
});
}
async generateDescription() {
const name = this.productForm.get('name')?.value;
const category = this.productForm.get('category')?.value;
if (!name || !category) {
alert('Inserisci nome e categoria prima di generare la descrizione');
return;
}
this.isGenerating.set(true);
const messages = [
{
role: 'system' as const,
content: 'Sei un esperto di marketing che crea descrizioni accattivanti per prodotti.'
},
{
role: 'user' as const,
content: `Crea una descrizione professionale per questo prodotto:
Nome: ${name}
Categoria: ${category}
La descrizione deve essere di circa 100 parole, coinvolgente e focalizzata sui benefici.`
}
];
try {
const response = await this.aiService.generateCompletion(messages).toPromise();
this.aiSuggestion.set(response.message);
} catch (error) {
console.error('Errore generazione:', error);
} finally {
this.isGenerating.set(false);
}
}
applySuggestion() {
this.productForm.patchValue({
description: this.aiSuggestion()
});
this.aiSuggestion.set('');
}
}
Implementa una cache per evitare chiamate ridondanti:
// ai-cache.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AICacheService {
private cache = new Map<string, { data: any; timestamp: number }>();
private readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minuti
get(key: string): any | null {
const cached = this.cache.get(key);
if (!cached) return null;
const isExpired = Date.now() - cached.timestamp > this.CACHE_DURATION;
if (isExpired) {
this.cache.delete(key);
return null;
}
return cached.data;
}
set(key: string, data: any): void {
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
clear(): void {
this.cache.clear();
}
// Genera chiave cache da messaggi
generateKey(messages: any[]): string {
return JSON.stringify(messages);
}
}
import{retry,catchError}from'rxjs/operators';
generateCompletionWithRetry(messages: AIMessage[]) {
return this.aiService.generateCompletion(messages).pipe(
retry({
count: 3,
delay: (error, retryCount) => {
// Backoff esponenziale
const delayMs = Math.min(1000 * Math.pow(2, retryCount), 10000);
return timer(delayMs);
}
}),
catchError(error => {
// Logging e gestione errore
this.logError(error);
return of({ message: 'Servizio temporaneamente non disponibile', tokens: 0 });
})
);
}
Per risposte più fluide, implementa lo streaming:
generateStreamingCompletion(messages: AIMessage[]): Observable {
return new Observable(observer => {
const eventSource = new EventSource(
`${this.apiUrl}/stream?messages=${encodeURIComponent(JSON.stringify(messages))}`
);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
observer.next(data.content);
if (data.done) {
eventSource.close();
observer.complete();
}
};
eventSource.onerror = (error) => {
eventSource.close();
observer.error(error);
};
return () => eventSource.close();
});
}
Non dimenticare i test! Ecco un esempio con Jest:
// chat-assistant.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChatAssistantComponent } from './chat-assistant.component';
import { AIService } from './services/ai.service';
import { of, throwError } from 'rxjs';
describe('ChatAssistantComponent', () => {
let component: ChatAssistantComponent;
let fixture: ComponentFixture<ChatAssistantComponent>;
let aiServiceSpy: jasmine.SpyObj<AIService>;
beforeEach(async () => {
const spy = jasmine.createSpyObj('AIService', ['generateCompletion']);
await TestBed.configureTestingModule({
imports: [ChatAssistantComponent],
providers: [
{ provide: AIService, useValue: spy }
]
}).compileComponents();
aiServiceSpy = TestBed.inject(AIService) as jasmine.SpyObj<AIService>;
fixture = TestBed.createComponent(ChatAssistantComponent);
component = fixture.componentInstance;
});
it('should send message and receive AI response', async () => {
const mockResponse = {
message: 'Risposta AI',
tokens: 50,
model: 'gpt-4'
};
aiServiceSpy.generateCompletion.and.returnValue(of(mockResponse));
component.userInput.set('Ciao');
await component.sendMessage();
expect(component.messages().length).toBe(3); // iniziale + user + AI
expect(component.messages()[2].content).toBe('Risposta AI');
});
it('should handle errors gracefully', async () => {
aiServiceSpy.generateCompletion.and.returnValue(
throwError(() => new Error('Network error'))
);
component.userInput.set('Test');
await component.sendMessage();
const lastMessage = component.messages()[component.messages().length - 1];
expect(lastMessage.content).toContain('errore');
});
});
Alcuni consigli per ottimizzare prestazioni e costi:
@Injectable({ providedIn: 'root' })
export class AIUsageTracker {
private totalTokens = signal(0);
private totalCalls = signal(0);
private estimatedCost = computed(() => {
// GPT-4: $0.03 per 1K tokens (esempio)
return (this.totalTokens() / 1000) * 0.03;
});
trackUsage(tokens: number) {
this.totalTokens.update(t => t + tokens);
this.totalCalls.update(c => c + 1);
}
getStats() {
return {
tokens: this.totalTokens(),
calls: this.totalCalls(),
cost: this.estimatedCost()
};
}
}
Integrare l'AI in Angular nel 2025 è più accessibile che mai grazie a:
Le possibilità sono infinite: da chatbot intelligenti a form assistiti, da analisi predittive a generazione contenuti. La chiave è iniziare con casi d'uso concreti, testare accuratamente e iterare basandosi sul feedback degli utenti.
Il futuro dello sviluppo web è AI-augmented, e Angular ti fornisce tutti gli strumenti per costruirlo oggi. Contattaci se hai bisogno di una soluzione su misura!