Claude Code para Angular: CLI, componentes standalone, formularios, HTTP y pruebas
Flujo con Claude Code para Angular CLI, componentes standalone, Reactive Forms, HttpClient, tests, E2E y revisión.
Claude Code aporta más valor en Angular cuando no se usa como generador de componentes aislados, sino como asistente que entiende el proyecto. Un cambio real suele tocar Angular CLI, componentes standalone, Reactive Forms, un servicio tipado con HttpClient, pruebas unitarias, pruebas E2E y una revisión final del diff.
Un standalone component es un componente que declara sus propias dependencias en imports, sin depender de un NgModule para quedar disponible. Reactive Forms es el enfoque donde el modelo del formulario y sus validaciones viven de forma explícita en TypeScript. HttpClient es la API oficial de Angular para hablar con servicios backend. Si estos conceptos aparecen en el prompt, Claude Code tiene muchas menos probabilidades de volver a patrones antiguos.
Usa documentación oficial como referencia: Angular CLI, Reactive forms, HttpClient, Angular testing y Claude Code common workflows. Para ampliar el flujo dentro de ClaudeCodeLab, revisa técnicas TypeScript, estrategias de testing y buenas prácticas de CLAUDE.md.
Define el marco de trabajo
El primer paso no es pedir código. Es pedir contexto. Claude Code necesita un harness, es decir, un marco operativo seguro: archivos permitidos, convenciones, comandos de verificación y errores que debe evitar.
Lee este proyecto Angular antes de editar. Revisa package.json, angular.json y src/app.
Indica si usa standalone components, qué runner de tests está configurado,
dónde se provee HttpClient y qué convenciones de nombres detectas.
No modifiques archivos todavía.
| Área | Trabajo para Claude Code | Decisión humana |
|---|---|---|
| Angular CLI | comandos de generación, rutas, dependencias | ownership de rutas y nombres |
| Component | imports, template, signals, eventos | copy de UI, accesibilidad, riesgo |
| Reactive Forms | FormGroup, validators, estado de envío | reglas de negocio |
| Service / HTTP | tipos, métodos API, backend de prueba | contrato, auth, errores |
| Tests | unit, E2E, regresiones | cobertura y criterio de merge |
flowchart LR
P[Prompt] --> C[Angular CLI]
C --> A[Standalone component]
A --> F[Reactive Forms]
A --> S[Ticket service]
S --> H[HttpClient]
F --> U[Unit tests]
A --> E[E2E tests]
U --> R[Review]
E --> R
Crea una base verificable con Angular CLI
Para un experimento nuevo, Angular CLI te da una estructura limpia. En un repositorio existente, no copies los comandos sin mirar el proyecto; pide a Claude Code que lea la configuración primero.
npm install -g @angular/cli
ng new support-desk-angular --standalone --routing --style css
cd support-desk-angular
ng generate component features/tickets/ticket-intake --standalone
ng generate service data/ticket
ng test
Ejecutar ng test antes de editar evita una trampa común: mezclar fallos antiguos con fallos introducidos por la nueva tarea. En equipos con trabajo paralelo, añade una restricción clara: solo tocar el feature acordado y no revertir cambios no confirmados de otras personas.
Empieza por el service tipado
Antes del formulario, define la capa de API. Así el componente se limita a estado visual y comportamiento de usuario. Este ejemplo puede vivir en src/app/data/ticket.service.ts.
import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
export type TicketPriority = 'low' | 'medium' | 'high';
export interface TicketDraft {
title: string;
email: string;
priority: TicketPriority;
message: string;
}
export interface Ticket extends TicketDraft {
id: string;
createdAt: string;
}
@Injectable({ providedIn: 'root' })
export class TicketService {
private readonly http = inject(HttpClient);
private readonly baseUrl = '/api/tickets';
listTickets(): Observable<Ticket[]> {
return this.http.get<Ticket[]>(this.baseUrl);
}
createTicket(draft: TicketDraft): Observable<Ticket> {
return this.http.post<Ticket>(this.baseUrl, draft);
}
}
En una app standalone, HttpClient se configura con provideHttpClient().
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideHttpClient()],
};
El error típico es dejar que el service mezcle endpoint, mensajes de UI, estado local y política de reintentos. Mantén el service aburrido: tipos, URL y comunicación. La pantalla decide cómo explicar un error al usuario.
Implementa el formulario standalone
El componente usa Reactive Forms para que el estado sea explícito y testeable. También usa signal para el estado de guardado.
import { Component, computed, inject, signal } from '@angular/core';
import { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';
import { finalize } from 'rxjs';
import { Ticket, TicketService, TicketPriority } from '../../../data/ticket.service';
@Component({
selector: 'app-ticket-intake',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()" aria-label="Support ticket form">
<label>Title <input type="text" formControlName="title" /></label>
<label>Email <input type="email" formControlName="email" /></label>
<label>
Priority
<select formControlName="priority">
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</label>
<label>Message <textarea formControlName="message"></textarea></label>
@if (form.hasError('submit')) {
<p role="alert">Ticket could not be saved. Please try again.</p>
}
@if (savedTicket(); as ticket) {
<p role="status">Saved ticket {{ ticket.id }}</p>
}
<button type="submit" [disabled]="isSubmitDisabled()">
{{ saving() ? 'Saving...' : 'Create ticket' }}
</button>
</form>
`,
})
export class TicketIntakeComponent {
private readonly ticketService = inject(TicketService);
readonly saving = signal(false);
readonly savedTicket = signal<Ticket | null>(null);
readonly form = new FormGroup({
title: new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.minLength(5)] }),
email: new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.email] }),
priority: new FormControl<TicketPriority>('medium', { nonNullable: true }),
message: new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.minLength(20)] }),
});
readonly isSubmitDisabled = computed(() => this.form.invalid || this.saving());
submit(): void {
this.form.markAllAsTouched();
this.form.setErrors(null);
if (this.form.invalid) return;
this.saving.set(true);
this.ticketService.createTicket(this.form.getRawValue()).pipe(
finalize(() => this.saving.set(false)),
).subscribe({
next: (ticket) => {
this.savedTicket.set(ticket);
this.form.reset({ title: '', email: '', priority: 'medium', message: '' });
},
error: () => this.form.setErrors({ submit: true }),
});
}
}
Tres fallos concretos aparecen mucho: mezclar ngModel con formControlName, no bloquear el doble envío mientras se guarda y mandar form.value al API con valores potencialmente nulos. La combinación de controles nonNullable y getRawValue() reduce ese riesgo.
Casos de uso prácticos
Caso uno: formulario de back office. El prompt debe listar campos, reglas de validación, estado de guardado, mensaje de éxito y error. “Hazlo moderno” no sirve como requisito.
Caso dos: extraer llamadas HTTP desde un componente antiguo. Pide tipos, service, separación de copy de UI y test con HttpTestingController.
Caso tres: corregir un bug con regresión. Si un fallo de API deja el botón deshabilitado, entrega pasos de reproducción, resultado esperado y comando de test. La solución debe incluir prueba de error.
Caso cuatro: migración gradual a standalone. Limita el cambio a un feature folder, conserva routing y providers, y ejecuta tests después de cada paso.
Tests y revisión crítica
Para HttpClient, las pruebas no deben salir a la red. Usa el backend de testing de Angular.
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
import { TicketService } from './ticket.service';
describe('TicketService', () => {
let service: TicketService;
let http: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [TicketService, provideHttpClient(), provideHttpClientTesting()],
});
service = TestBed.inject(TicketService);
http = TestBed.inject(HttpTestingController);
});
afterEach(() => http.verify());
it('creates a ticket through the API', () => {
const draft = {
title: 'Billing export is stuck',
email: 'ops@example.com',
priority: 'high' as const,
message: 'The monthly billing export has not finished for two hours.',
};
service.createTicket(draft).subscribe((ticket) => expect(ticket.id).toBe('T-100'));
const req = http.expectOne('/api/tickets');
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual(draft);
req.flush({ ...draft, id: 'T-100', createdAt: '2026-06-02T09:00:00.000Z' });
});
});
Para E2E, usa ng e2e si el workspace tiene target configurado, o Playwright si el equipo ya lo usa. Lo importante es cubrir el camino real: cargar pantalla, rellenar campos, interceptar API y verificar el mensaje de éxito.
Revisa críticamente este diff de Angular.
Comprueba imports/providers standalone, valores nullable en Reactive Forms,
doble envío, responsabilidades entre service y UI, tests de HttpClient,
cobertura unit/E2E, uso de any, subscriptions innecesarias y accesibilidad.
Devuelve problemas con archivo, línea y prioridad.
Resultado y CTA
Probé este flujo en un proyecto Angular pequeño. Un prompt vago produjo mezcla de ngModel, service con copy de UI y ninguna prueba de fallo. Al dividir la tarea en inspección, service, formulario, test HTTP, E2E y review, el diff fue mucho más fácil de revisar. Las restricciones más útiles fueron “no uses any”, “no pongas copy de UI en el service” y “usa Reactive Forms solamente”.
Si quieres llevar este patrón a un equipo, documenta las reglas en CLAUDE.md y conecta el proceso con formación y consultoría Claude Code. Para lectores que empiezan, la chuleta gratuita es un buen siguiente paso antes de automatizar revisiones.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.