Angular: fundamentos del framework
Contenido
- Introducción
- Conocimiento previo
- Referencias
- Índice
- 1. Qué aporta Angular frente a JavaScript puro y React
- 2. Preparar el entorno de trabajo
- 3. Crear una aplicación Angular
- 4. Estructura inicial del proyecto
- 5. Componentes standalone
- 6. Plantillas: interpolación y expresiones
- 7. Property binding y event binding
- 8. Control flow moderno con @if y @for
- 9. Señales para estado local
- 10. Props de Angular: entradas y salidas
- 11. Binding bidireccional con formularios simples
- 12. Servicios e inyección de dependencias
- 13. Organización básica de una aplicación Angular
- 14. Proyecto guiado: gestor de tareas con Angular
- 14.1. Definir el modelo de tarea
- 14.2. Crear un servicio de tareas
- 14.3. Crear el componente de formulario
- 14.4. Crear la lista de tareas
- 14.5. Componer la aplicación
- 15. Buenas prácticas iniciales
- 16. Ejercicios
Tutorial complementario de Angular para Desarrollo web en entorno cliente: entorno de trabajo, componentes standalone, TypeScript, plantillas, binding, control flow, señales, formularios, servicios e inyección de dependencias.
Introducción
Angular es un framework de desarrollo web para crear aplicaciones cliente con TypeScript. A diferencia de React, que se presenta como una biblioteca centrada en la interfaz, Angular ofrece una estructura más completa desde el principio: componentes, plantillas, inyección de dependencias, formularios, rutas, servicios, HTTP y herramientas oficiales de proyecto.
Este tutorial es complementario dentro de la asignatura Desarrollo web en entorno cliente. Su objetivo no es sustituir lo aprendido con JavaScript, DOM, asincronía o React, sino mostrar otro enfoque profesional para construir aplicaciones cliente. Angular resulta especialmente útil cuando se trabaja en proyectos grandes, con equipos que necesitan convenciones claras y una arquitectura homogénea.
Trabajaremos con el enfoque actual de Angular: componentes standalone, TypeScript, plantillas declarativas, control flow moderno con @if y @for, señales para estado local y servicios con inyección de dependencias.
Conocimiento previo
- HTML
- JavaScript
- JavaScript en el navegador: DOM, eventos, formularios y comunicación asíncrona
- Librerías y frameworks de cliente
- React: fundamentos para aplicaciones cliente
- Conocimientos básicos de TypeScript recomendables.
- Uso básico de terminal, Node.js y npm.
Referencias
- Angular - Docs
- Angular - Essentials
- Angular - Components
- Angular - Templates
- Angular - Signals
- Angular - Forms
- Angular - Dependency injection
- TypeScript Handbook
Índice
- Qué aporta Angular frente a JavaScript puro y React
- Preparar el entorno de trabajo
- Crear una aplicación Angular
- Estructura inicial del proyecto
- Componentes standalone
- Plantillas: interpolación y expresiones
- Property binding y event binding
- Control flow moderno con @if y @for
- Señales para estado local
- Props de Angular: entradas y salidas
- Binding bidireccional con formularios simples
- Servicios e inyección de dependencias
- Organización básica de una aplicación Angular
- Proyecto guiado: gestor de tareas con Angular 14.1. Definir el modelo de tarea 14.2. Crear un servicio de tareas 14.3. Crear el componente de formulario 14.4. Crear la lista de tareas 14.5. Componer la aplicación
- Buenas prácticas iniciales
- Ejercicios
1. Qué aporta Angular frente a JavaScript puro y React
Con JavaScript puro puedes manipular el DOM, escuchar eventos y consumir APIs directamente. Ese enfoque es muy útil para comprender cómo funciona el navegador. Con React aprendiste a dividir la interfaz en componentes y a renderizar en función del estado. Angular comparte esa idea de componentes, pero añade una estructura de framework más completa.
Comparación inicial:
| Enfoque | Características principales |
|---|---|
| JavaScript puro | Control directo del DOM, mínima abstracción, ideal para aprender fundamentos. |
| React | Biblioteca de interfaz basada en componentes, JSX, estado y ecosistema flexible. |
| Angular | Framework completo con TypeScript, plantillas, servicios, rutas, formularios, HTTP e inyección de dependencias. |
Angular destaca cuando conviene que el proyecto tenga convenciones comunes desde el inicio. Por ejemplo, en una aplicación empresarial es habitual necesitar rutas, formularios complejos, comunicación HTTP, servicios compartidos, validación, interceptores y organización por funcionalidades. Angular proporciona piezas oficiales para todo ello.
Esto no significa que Angular sea siempre mejor. Para una interfaz pequeña puede ser excesivo. Para una aplicación amplia con muchas pantallas y equipo de desarrollo, su estructura puede ser una ventaja.
2. Preparar el entorno de trabajo
Para trabajar con Angular necesitas Node.js, npm y la CLI de Angular. Comprueba primero las versiones instaladas:
node --versionnpm --versionLa CLI de Angular permite crear proyectos, generar componentes y ejecutar el servidor de desarrollo.
Puedes crear una aplicación usando npx, sin instalar la CLI globalmente:
npx @angular/cli new gestor-tareasTambién puedes instalar la CLI globalmente si el entorno del centro lo permite:
npm install -g @angular/cling version3. Crear una aplicación Angular
Para crear la aplicación:
npx @angular/cli new gestor-tareasDurante la creación, la CLI puede preguntar por opciones como estilos, rutas o configuración. Para este primer proyecto puedes aceptar una configuración sencilla y usar CSS.
Entra en la carpeta y arranca el servidor:
cd gestor-tareasnpm startO bien:
ng serveLa aplicación se abre normalmente en una dirección local como:
http://localhost:4200Angular recompila la aplicación cuando modificas los archivos, de forma parecida a lo que viste con Vite en React.
4. Estructura inicial del proyecto
Una aplicación Angular moderna puede tener una estructura similar a esta:
src/ app/ app.config.ts app.css app.html app.routes.ts app.ts index.html main.tsArchivos importantes:
| Archivo | Función |
|---|---|
main.ts | Punto de entrada de la aplicación. Arranca Angular en el navegador. |
app.ts | Componente principal. |
app.html | Plantilla del componente principal. |
app.css | Estilos del componente principal. |
app.config.ts | Configuración global de la aplicación. |
app.routes.ts | Definición de rutas si el proyecto las usa. |
Angular separa con claridad la lógica TypeScript, la plantilla HTML y los estilos del componente. También permite escribir plantillas y estilos dentro del decorador, pero en aplicaciones reales suele ser más cómodo separarlos.
5. Componentes standalone
Un componente Angular agrupa tres piezas:
- Clase TypeScript con datos y métodos.
- Plantilla HTML con la interfaz.
- Estilos asociados al componente.
Ejemplo de componente standalone:
import { Component } from '@angular/core';
@Component({ selector: 'app-saludo', templateUrl: './saludo.html', styleUrl: './saludo.css',})export class Saludo { nombre = 'Ada';}Plantilla saludo.html:
<section> <h2>Hola, {{ nombre }}</h2> <p>Este es un componente Angular.</p></section>El selector indica cómo se usa el componente desde otra plantilla:
<app-saludo></app-saludo>Si un componente usa otros componentes, directivas o módulos, los declara en imports:
import { Component } from '@angular/core';import { Saludo } from './saludo/saludo';
@Component({ selector: 'app-root', imports: [Saludo], templateUrl: './app.html',})export class App {}6. Plantillas: interpolación y expresiones
La interpolación permite mostrar valores de la clase en la plantilla:
export class ProductoCard { nombre = 'Teclado mecánico'; precio = 49.95;}<article> <h2>{{ nombre }}</h2> <p>{{ precio }} €</p></article>Dentro de {{ }} puedes usar expresiones sencillas:
<p>Precio con IVA: {{ precio * 1.21 }} €</p><p>{{ nombre.toUpperCase() }}</p>Conviene evitar expresiones complejas en la plantilla. Si una operación requiere varias condiciones o transformaciones, es mejor prepararla en la clase TypeScript.
7. Property binding y event binding
El property binding conecta una propiedad del DOM o de un componente con un valor de la clase:
export class ImagenProducto { imagenUrl = '/img/teclado.webp'; descripcion = 'Teclado mecánico retroiluminado';}<img [src]="imagenUrl" [alt]="descripcion" />El event binding ejecuta un método cuando ocurre un evento:
export class Contador { total = 0;
incrementar() { this.total += 1; }}<p>Total: {{ total }}</p><button type="button" (click)="incrementar()">Sumar</button>Comparación con React:
| React | Angular |
|---|---|
onClick={manejarClick} | (click)="manejarClick()" |
src={imagenUrl} | [src]="imagenUrl" |
{nombre} | {{ nombre }} |
8. Control flow moderno con @if y @for
Angular permite escribir condiciones y bucles en la plantilla. El control flow moderno usa @if, @else, @for y @switch.
Condicional:
@if (usuarioActivo) { <p>Usuario conectado</p>} @else { <p>Usuario invitado</p>}Lista con @for:
export class ListaProductos { productos = [ { id: 1, nombre: 'Teclado' }, { id: 2, nombre: 'Ratón' }, { id: 3, nombre: 'Monitor' }, ];}<ul> @for (producto of productos; track producto.id) { <li>{{ producto.nombre }}</li> } @empty { <li>No hay productos.</li> }</ul>La parte track producto.id cumple una función parecida a la key de React: ayuda a Angular a identificar cada elemento de la lista de forma estable.
9. Señales para estado local
Las señales permiten guardar valores reactivos. Una señal se lee como una función y se actualiza con métodos como set o update.
import { Component, signal } from '@angular/core';
@Component({ selector: 'app-contador', templateUrl: './contador.html',})export class Contador { total = signal(0);
incrementar() { this.total.update((valor) => valor + 1); }
reiniciar() { this.total.set(0); }}Plantilla:
<p>Total: {{ total() }}</p><button type="button" (click)="incrementar()">Sumar</button><button type="button" (click)="reiniciar()">Reiniciar</button>Observa que en la plantilla se lee total() y no total. Esa llamada obtiene el valor actual de la señal.
Las señales son útiles para estado local de componentes: contadores, filtros, campos, selección actual o visibilidad de un panel.
10. Props de Angular: entradas y salidas
En React usaste props para pasar datos de padre a hijo. En Angular se usan entradas y salidas. Una entrada permite que el componente reciba datos. Una salida permite que el componente avise de un evento al padre.
Ejemplo de componente hijo:
import { Component, input, output } from '@angular/core';
type Producto = { id: number; nombre: string; precio: number;};
@Component({ selector: 'app-producto-card', templateUrl: './producto-card.html',})export class ProductoCard { producto = input.required<Producto>(); eliminar = output<number>();
avisarEliminacion() { this.eliminar.emit(this.producto().id); }}Plantilla del hijo:
<article> <h2>{{ producto().nombre }}</h2> <p>{{ producto().precio }} €</p> <button type="button" (click)="avisarEliminacion()">Eliminar</button></article>Uso desde el padre:
<app-producto-card [producto]="producto" (eliminar)="eliminarProducto($event)"></app-producto-card>Relación conceptual:
| Comunicación | React | Angular |
|---|---|---|
| Padre a hijo | Props | Entradas |
| Hijo a padre | Función recibida por props | Salidas con eventos |
11. Binding bidireccional con formularios simples
Para formularios sencillos, Angular permite usar [(ngModel)]. Este patrón conecta el valor de un input con una propiedad del componente en ambos sentidos.
Primero importa FormsModule en el componente:
import { Component } from '@angular/core';import { FormsModule } from '@angular/forms';
@Component({ selector: 'app-formulario-nombre', imports: [FormsModule], templateUrl: './formulario-nombre.html',})export class FormularioNombre { nombre = '';}Plantilla:
<label for="nombre">Nombre</label><input id="nombre" type="text" [(ngModel)]="nombre" />
<p>Hola, {{ nombre }}</p>[(ngModel)] combina lectura y escritura. Es parecido a un formulario controlado en React, pero con sintaxis propia de Angular.
Para formularios grandes, validaciones complejas o lógica avanzada, Angular ofrece formularios reactivos. Se trabajarán en el tutorial complementario avanzado.
12. Servicios e inyección de dependencias
Un servicio es una clase que concentra lógica reutilizable que no pertenece a la presentación de un componente. Por ejemplo: gestionar tareas, acceder a una API, guardar preferencias o compartir datos.
Servicio básico:
import { Injectable } from '@angular/core';
export type Tarea = { id: number; titulo: string; completada: boolean;};
@Injectable({ providedIn: 'root' })export class TareasService { private tareas: Tarea[] = [ { id: 1, titulo: 'Repasar componentes', completada: false }, { id: 2, titulo: 'Practicar plantillas', completada: true }, ];
obtenerTareas() { return this.tareas; }}Uso desde un componente con inject:
import { Component, inject } from '@angular/core';import { TareasService } from './tareas.service';
@Component({ selector: 'app-lista-tareas', templateUrl: './lista-tareas.html',})export class ListaTareas { private tareasService = inject(TareasService);
tareas = this.tareasService.obtenerTareas();}La inyección de dependencias permite que Angular proporcione el servicio donde se necesita, sin crear manualmente instancias con new dentro de los componentes.
13. Organización básica de una aplicación Angular
Una estructura inicial razonable para una aplicación pequeña puede ser:
src/app/ components/ tarea-form/ tarea-list/ tarea-item/ services/ tareas.service.ts models/ tarea.ts app.ts app.html app.config.tsResponsabilidades:
| Carpeta | Contenido |
|---|---|
components | Componentes visuales y de interacción. |
services | Lógica compartida, datos, comunicación con APIs. |
models | Tipos e interfaces TypeScript. |
app.* | Componente raíz y configuración principal. |
No conviene crear una estructura enorme para un proyecto pequeño. Empieza simple y separa cuando el código empiece a crecer.
14. Proyecto guiado: gestor de tareas con Angular
El proyecto de este tutorial será un gestor de tareas. Permitirá listar tareas, añadir nuevas tareas y marcar tareas como completadas. Usará componentes, señales, formularios simples, servicios e inyección de dependencias.
14.1. Definir el modelo de tarea
Archivo models/tarea.ts:
export type Tarea = { id: number; titulo: string; completada: boolean;};Definir tipos ayuda a detectar errores antes de ejecutar la aplicación.
14.2. Crear un servicio de tareas
Archivo services/tareas.service.ts:
import { Injectable, signal } from '@angular/core';import { Tarea } from '../models/tarea';
@Injectable({ providedIn: 'root' })export class TareasService { tareas = signal<Tarea[]>([ { id: 1, titulo: 'Crear primer componente', completada: true }, { id: 2, titulo: 'Practicar binding', completada: false }, ]);
agregar(titulo: string) { const nuevaTarea: Tarea = { id: Date.now(), titulo, completada: false, };
this.tareas.update((tareas) => [...tareas, nuevaTarea]); }
alternar(id: number) { this.tareas.update((tareas) => tareas.map((tarea) => tarea.id === id ? { ...tarea, completada: !tarea.completada } : tarea, ), ); }}El servicio mantiene la lista de tareas y ofrece métodos para modificarla.
14.3. Crear el componente de formulario
Archivo components/tarea-form/tarea-form.ts:
import { Component, inject } from '@angular/core';import { FormsModule } from '@angular/forms';import { TareasService } from '../../services/tareas.service';
@Component({ selector: 'app-tarea-form', imports: [FormsModule], templateUrl: './tarea-form.html',})export class TareaForm { private tareasService = inject(TareasService); titulo = '';
agregarTarea() { const tituloLimpio = this.titulo.trim();
if (tituloLimpio.length === 0) { return; }
this.tareasService.agregar(tituloLimpio); this.titulo = ''; }}Plantilla tarea-form.html:
<form (ngSubmit)="agregarTarea()"> <label for="titulo">Nueva tarea</label> <input id="titulo" name="titulo" [(ngModel)]="titulo" /> <button type="submit">Añadir</button></form>El atributo name es necesario cuando se usa ngModel dentro de un formulario.
14.4. Crear la lista de tareas
Archivo components/tarea-list/tarea-list.ts:
import { Component, inject } from '@angular/core';import { TareasService } from '../../services/tareas.service';
@Component({ selector: 'app-tarea-list', templateUrl: './tarea-list.html',})export class TareaList { tareasService = inject(TareasService);}Plantilla tarea-list.html:
<ul> @for (tarea of tareasService.tareas(); track tarea.id) { <li> <label> <input type="checkbox" [checked]="tarea.completada" (change)="tareasService.alternar(tarea.id)" /> {{ tarea.titulo }} </label> </li> } @empty { <li>No hay tareas pendientes.</li> }</ul>La plantilla lee la señal con tareasService.tareas().
14.5. Componer la aplicación
Archivo app.ts:
import { Component } from '@angular/core';import { TareaForm } from './components/tarea-form/tarea-form';import { TareaList } from './components/tarea-list/tarea-list';
@Component({ selector: 'app-root', imports: [TareaForm, TareaList], templateUrl: './app.html',})export class App {}Plantilla app.html:
<main> <h1>Gestor de tareas</h1> <app-tarea-form></app-tarea-form> <app-tarea-list></app-tarea-list></main>El resultado muestra una aplicación pequeña, pero ya contiene las ideas esenciales de Angular: componentes, plantillas, formularios, servicios, señales e inyección de dependencias.
15. Buenas prácticas iniciales
- Usa TypeScript para definir la forma de los datos.
- Mantén componentes con responsabilidades claras.
- Usa servicios para lógica compartida o acceso a datos.
- No manipules el DOM manualmente si Angular puede renderizar la plantilla.
- Usa
@ify@forpara condicionales y listas en contenido nuevo. - Usa
trackcon identificadores estables en listas. - Asocia siempre los campos de formulario con
label. - Evita mezclar demasiada lógica dentro de la plantilla.
- No copies ejemplos antiguos de Angular sin revisar la documentación actual.
16. Ejercicios
Ejercicio 1: Crear una aplicación Angular
Crea una aplicación Angular nueva, arráncala en local y localiza los archivos main.ts, app.ts, app.html y app.config.ts.
Ejercicio 2: Primer componente standalone
Crea un componente Saludo que muestre un nombre y un mensaje. Úsalo desde el componente principal.
Ejercicio 3: Interpolación y property binding
Crea un componente ProductoCard que muestre nombre, precio e imagen usando interpolación y property binding.
Ejercicio 4: Event binding
Crea un contador con botones para sumar, restar y reiniciar usando (click).
Ejercicio 5: Control flow con @if y @for
Renderiza una lista de productos con @for y muestra un mensaje alternativo con @empty cuando la lista esté vacía.
Ejercicio 6: Señales
Reescribe el contador usando signal, set y update.
Ejercicio 7: Entrada y salida de componente
Crea un componente hijo que reciba un producto como entrada y emita un evento cuando el usuario pulse “Eliminar”.
Ejercicio 8: Formulario con ngModel
Crea un formulario simple con [(ngModel)] para escribir un nombre y mostrarlo en tiempo real.
Ejercicio 9: Servicio compartido
Crea un servicio que almacene una lista de tareas y consúmelo desde dos componentes distintos.
Ejercicio 10: Proyecto integrador
Construye el gestor de tareas de la unidad con servicio, señales, formulario, lista y marcado de tareas completadas.