TUTORIAL COMPLETO DE ANGULAR PARA DESARROLLADORES 2025
Introducción a Angular para Desarrolladores
Angular es un framework de JavaScript de código abierto mantenido por Google, diseñado para construir aplicaciones web dinámicas y escalables. En 2025, Angular sigue siendo una opción líder para desarrolladores que buscan crear interfaces de usuario robustas y mantenibles. Este tutorial está dirigido a programadores que desean dominar Angular, desde la configuración inicial hasta la creación de aplicaciones complejas. A través de ejemplos prácticos, exploraremos cómo estructurar proyectos, trabajar con componentes, gestionar servicios y optimizar el rendimiento. Si buscas aprender Angular desde cero o mejorar tus habilidades, este artículo te proporcionará una guía completa.
Para comenzar, asegúrate de tener instalado Node.js (versión 18 o superior) y npm. Luego, instala la CLI de Angular globalmente ejecutando el siguiente comando:
npm install -g @angular/cli
Este comando instala la herramienta oficial para crear y gestionar proyectos Angular.
Configuración de un Proyecto Angular
El primer paso para trabajar con Angular es crear un nuevo proyecto. La CLI de Angular simplifica este proceso al generar una estructura de carpetas preconfigurada. Ejecuta el siguiente comando para crear un proyecto llamado “mi-app”:
ng new mi-app
La CLI te preguntará si deseas incluir enrutamiento y qué formato de estilos prefieres (CSS, SCSS, etc.). Para este tutorial, selecciona enrutamiento y SCSS. Una vez creado, el proyecto tendrá la siguiente estructura:
mi-app/
├── src/
│ ├── app/
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── app-routing.module.ts
│ ├── assets/
│ └── styles.scss
├── angular.json
├── package.json
└── tsconfig.json
Navega al directorio del proyecto y ejecuta la aplicación:
cd mi-app
ng serve
La aplicación estará disponible en http://localhost:4200. Este comando inicia un servidor de desarrollo con recarga automática, ideal para probar cambios en tiempo real.
Creación de Componentes en Angular
Los componentes son los bloques fundamentales de una aplicación Angular. Cada componente encapsula una parte de la interfaz de usuario, con su propia lógica y estilos. Para crear un componente llamado “header”, usa la CLI:
ng generate component header
Esto genera una carpeta header con los siguientes archivos:
src/app/header/
├── header.component.html
├── header.component.scss
├── header.component.spec.ts
└── header.component.ts
Edita el archivo header.component.html para definir la estructura del componente:
<header>
<h1>Bienvenido a Mi App</h1>
<nav>
<a routerLink="/">Inicio</a>
<a routerLink="/acerca">Acerca</a>
</nav>
</header>
Luego, incluye el componente en app.component.html:
<app-header></app-header> <router-outlet></router-outlet>
El elemento <router-outlet> indica dónde se renderizarán las vistas asociadas a las rutas. Este enfoque permite crear interfaces modulares y reutilizables.
Gestión de Rutas en Angular
El enrutamiento es esencial para aplicaciones de página única (SPA). Angular proporciona un módulo de enrutamiento que permite definir rutas y asociarlas a componentes. Edita el archivo app-routing.module.ts para configurar las rutas:
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { AboutComponent } from "./about/about.component";
const routes: Routes = [
{ path: "", component: HomeComponent },
{ path: "acerca", component: AboutComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Crea los componentes home y about con la CLI:
ng generate component home
ng generate component about
Las rutas definidas permiten navegar entre las páginas “Inicio” y “Acerca” sin recargar la aplicación, mejorando la experiencia del usuario.
Uso de Servicios para Lógica Compartida
Los servicios en Angular permiten compartir lógica y datos entre componentes. Son ideales para manejar comunicaciones con APIs o gestionar estados. Crea un servicio llamado data con la CLI:
ng generate service data
Edita data.service.ts para incluir un método que obtenga datos simulados:
import { Injectable } from "@angular/core";
import { Observable, of } from "rxjs";
@Injectable({
providedIn: "root",
})
export class DataService {
getItems(): Observable<string[]> {
return of(["Item 1", "Item 2", "Item 3"]);
}
}
Inyecta el servicio en un componente, por ejemplo, home.component.ts:
import { Component, OnInit } from "@angular/core";
import { DataService } from "../data.service";
@Component({
selector: "app-home",
templateUrl: "./home.component.html",
})
export class HomeComponent implements OnInit {
items: string[] = [];
constructor(private dataService: DataService) {}
ngOnInit(): void {
this.dataService.getItems().subscribe((data) => {
this.items = data;
});
}
}
Muestra los datos en home.component.html:
<ul>
<li *ngFor="let item of items">{{ item }}</li>
</ul>
Este ejemplo demuestra cómo los servicios facilitan la gestión de datos compartidos en una aplicación Angular.
Directivas para Manipulación del DOM
Angular ofrece directivas para modificar el comportamiento y la apariencia del DOM. Las directivas estructurales, como *ngFor y *ngIf, son especialmente útiles. Por ejemplo, usa *ngIf para mostrar contenido condicionalmente:
<div *ngIf="items.length > 0">
<p>Hay elementos disponibles</p>
</div>
<div *ngIf="items.length === 0">
<p>No hay elementos</p>
</div>
Las directivas de atributo, como ngClass, permiten aplicar clases dinámicamente:
<div [ngClass]="{'active': isActive}">Elemento dinámico</div>
Define isActive en el componente:
export class HomeComponent {
isActive = true;
}
Estas herramientas permiten crear interfaces dinámicas sin manipular el DOM directamente.
Comunicación con APIs Externas
La mayoría de las aplicaciones Angular interactúan con APIs REST. Usa el módulo HttpClient para realizar solicitudes HTTP. Primero, importa HttpClientModule en app.module.ts:
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { HttpClientModule } from "@angular/common/http";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule, HttpClientModule],
bootstrap: [AppComponent],
})
export class AppModule {}
Actualiza data.service.ts para obtener datos de una API, como JSONPlaceholder:
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
@Injectable({
providedIn: "root",
})
export class DataService {
private apiUrl = "https://jsonplaceholder.typicode.com/posts";
constructor(private http: HttpClient) {}
getPosts(): Observable<any[]> {
return this.http.get<any[]>(this.apiUrl);
}
}
Usa el servicio en home.component.ts:
export class HomeComponent implements OnInit {
posts: any[] = [];
constructor(private dataService: DataService) {}
ngOnInit(): void {
this.dataService.getPosts().subscribe((data) => {
this.posts = data;
});
}
}
Muestra los resultados en home.component.html:
<div *ngFor="let post of posts">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
</div>
Este enfoque permite integrar datos externos de manera eficiente.
Formularios en Angular
Angular ofrece dos enfoques para manejar formularios: reactivos y basados en plantillas. Los formularios reactivos son más escalables y fáciles de probar. Crea un formulario reactivo en un nuevo componente contact:
ng generate component contact
Configura el formulario en contact.component.ts:
import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
@Component({
selector: "app-contact",
templateUrl: "./contact.component.html",
})
export class ContactComponent implements OnInit {
contactForm: FormGroup;
constructor(private fb: FormBuilder) {
this.contactForm = this.fb.group({
name: ["", Validators.required],
email: ["", [Validators.required, Validators.email]],
message: ["", Validators.required],
});
}
ngOnInit(): void {}
onSubmit(): void {
if (this.contactForm.valid) {
console.log(this.contactForm.value);
}
}
}
Define la plantilla en contact.component.html:
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
<div>
<label for="name">Nombre</label>
<input id="name" formControlName="name" />
<div
*ngIf="contactForm.get('name')?.invalid && contactForm.get('name')?.touched"
>
El nombre es requerido
</div>
</div>
<div>
<label for="email">Correo</label>
<input id="email" formControlName="email" />
<div
*ngIf="contactForm.get('email')?.invalid && contactForm.get('email')?.touched"
>
Ingresa un correo válido
</div>
</div>
<div>
<label for="message">Mensaje</label>
<textarea id="message" formControlName="message"></textarea>
<div
*ngIf="contactForm.get('message')?.invalid && contactForm.get('message')?.touched"
>
El mensaje es requerido
</div>
</div>
<button type="submit" [disabled]="contactForm.invalid">Enviar</button>
</form>
Importa ReactiveFormsModule en app.module.ts:
import { ReactiveFormsModule } from "@angular/forms";
@NgModule({
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
ReactiveFormsModule,
],
// ...
})
export class AppModule {}
Este formulario valida los campos y deshabilita el botón de envío hasta que todos sean válidos.
Gestión del Estado con NgRx
Para aplicaciones grandes, gestionar el estado puede ser un desafío. NgRx es una biblioteca popular para manejar estados reactivos en Angular. Instala NgRx:
ng add @ngrx/store
Crea un store para gestionar una lista de tareas. Define la interfaz del estado en todo.state.ts:
export interface Todo {
id: number;
title: string;
completed: boolean;
}
export interface TodoState {
todos: Todo[];
}
Crea acciones en todo.actions.ts:
import { createAction, props } from "@ngrx/store";
import { Todo } from "./todo.state";
export const addTodo = createAction(
"[Todo] Add Todo",
props<{ title: string }>()
);
export const toggleTodo = createAction(
"[Todo] Toggle Todo",
props<{ id: number }>()
);
Define el reductor en todo.reducer.ts:
import { createReducer, on } from "@ngrx/store";
import { TodoState } from "./todo.state";
import { addTodo, toggleTodo } from "./todo.actions";
export const initialState: TodoState = {
todos: [],
};
export const todoReducer = createReducer(
initialState,
on(addTodo, (state, { title }) => ({
...state,
todos: [
...state.todos,
{ id: state.todos.length + 1, title, completed: false },
],
})),
on(toggleTodo, (state, { id }) => ({
...state,
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
}))
);
Registra el store en app.module.ts:
import { StoreModule } from "@ngrx/store";
import { todoReducer } from "./todo.reducer";
@NgModule({
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
ReactiveFormsModule,
StoreModule.forRoot({ todos: todoReducer }),
],
// ...
})
export class AppModule {}
Usa el store en un componente todo.component.ts:
import { Component } from "@angular/core";
import { Store } from "@ngrx/store";
import { TodoState } from "./todo.state";
import { addTodo, toggleTodo } from "./todo.actions";
import { Observable } from "rxjs";
import { Todo } from "./todo.state";
@Component({
selector: "app-todo",
templateUrl: "./todo.component.html",
})
export class TodoComponent {
todos$: Observable<Todo[]>;
newTodo = "";
constructor(private store: Store<{ todos: TodoState }>) {
this.todos$ = store.select((state) => state.todos.todos);
}
addTodo(): void {
if (this.newTodo) {
this.store.dispatch(addTodo({ title: this.newTodo }));
this.newTodo = "";
}
}
toggleTodo(id: number): void {
this.store.dispatch(toggleTodo({ id }));
}
}
Crea la plantilla en todo.component.html:
<input
[(ngModel)]="newTodo"
(keyup.enter)="addTodo()"
placeholder="Nueva tarea"
/>
<ul>
<li *ngFor="let todo of todos$ | async">
<input
type="checkbox"
[checked]="todo.completed"
(change)="toggleTodo(todo.id)"
/>
{{ todo.title }}
</li>
</ul>
Este ejemplo muestra cómo NgRx simplifica la gestión del estado global en aplicaciones complejas.
Optimización del Rendimiento
El rendimiento es crítico en aplicaciones Angular. Usa las siguientes estrategias para optimizar tu proyecto:
- Lazy Loading: Carga módulos bajo demanda para reducir el tiempo de carga inicial. Configura el enrutamiento para cargar módulos de forma diferida:
const routes: Routes = [
{
path: "admin",
loadChildren: () =>
import("./admin/admin.module").then((m) => m.AdminModule),
},
];
- Change Detection: Usa la estrategia
OnPushen componentes para reducir verificaciones innecesarias:
@Component({
selector: "app-my-component",
templateUrl: "./my-component.component.html",
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MyComponent {}
- Tree Shaking: Asegúrate de que el compilador elimine código no utilizado. Evita importar módulos completos cuando solo necesitas partes específicas.
Estas técnicas mejoran la velocidad y la experiencia del usuario.
Pruebas en Angular
Angular tiene soporte integrado para pruebas unitarias y de integración. Usa Jasmine y Karma para pruebas unitarias. Genera un componente con pruebas:
ng generate component test --spec
Ejecuta las pruebas con:
ng test
Para pruebas de integración, usa Protractor o Cypress. Un ejemplo de prueba unitaria en test.component.spec.ts:
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { TestComponent } from "./test.component";
describe("TestComponent", () => {
let component: TestComponent;
let fixture: ComponentFixture<TestComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TestComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it("should create", () => {
expect(component).toBeTruthy();
});
});
Las pruebas garantizan que tu código sea robusto y mantenible.
Conclusiones
Angular es una herramienta poderosa para construir aplicaciones web modernas en 2025. Este tutorial ha cubierto los fundamentos, desde la configuración de un proyecto hasta la implementación de patrones avanzados como la gestión del estado con NgRx. Al dominar componentes, servicios, enrutamiento y optimización, puedes crear aplicaciones escalables y eficientes. Los ejemplos de código proporcionados ilustran cómo aplicar estos conceptos en proyectos reales. Continúa explorando la documentación oficial y la comunidad de Angular para mantenerte actualizado con las mejores prácticas de desarrollo y nuevas funcionalidades.