Apariencia
🔄 Clase 4 — Ciclo de Vida
📅 2026-07-18 · 🗂️ Proyecto:
AppClase04(destino en este repo:app-semana04) · 🎯 Ciclo de vida de un componente
Nuevo proyecto para el tema del día: el ciclo de vida de un componente — los momentos por los que pasa un componente Angular desde que se crea hasta que se destruye.
➡️ El mismo día se vio después Eventos en Angular (Event Binding, categorías de eventos) → ver Clase 5.
🗂️ Temas de esta clase
- Nuevo proyecto
AppClase04, componentesaludogenerado con Angular CLI dentro de una subcarpetacomponent/. - Ciclo de vida de un componente:
constructor()→ngOnChanges()→ngOnInit()→ (…) →ngOnDestroy(), conconsole.lognumerados para ver el orden real. @Input(): pasar un dato del componente padre (App) a uno hijo (Bienvenida), y cómo eso disparangOnChanges.
🆕 El proyecto y el componente saludo
Carpeta del proyecto:
~/angular/AppClase04(en la máquina del curso). Todavía no está copiado a02-Ejercicios/app-semana04/de este repo — pendiente para cuando termine la clase.Comando usado:
$ ng generate component component/saludo CREATE src/app/component/saludo/saludo.spec.ts (554 bytes) CREATE src/app/component/saludo/saludo.ts (197 bytes) CREATE src/app/component/saludo/saludo.css (0 bytes) CREATE src/app/component/saludo/saludo.html (22 bytes)Carpeta
component/como contenedor: en vez de crear el componente directo ensrc/app/saludo/, el profe lo pidió dentro de una subcarpetacomponent/(src/app/component/saludo/). Es una forma de agrupar todos los componentes hijos aparte del componente raíz (app.ts), útil cuando el proyecto crece y tiene varios.Nomenclatura moderna: los archivos se llaman
saludo.ts/saludo.html/saludo.css(sin el sufijo.component), igual que el componente raízapp.ts. → ver05-Estructura-proyecto.md(sección de nomenclatura moderna vs antigua).saludo.html(primer contenido, texto cortado en la captura):html<p>¡Hola! soy un componente y vamos a ver el ciclo de vida...</p>📝 Correcciones de la pizarra/código: faltaba el signo de apertura (
!Hola→¡Hola) y había una errataavmos→vamos. El texto sigue cortado en la captura (...ciclo de v) — falta ver cómo termina la frase.
🧩 saludo.ts: la clase implementa OnInit y OnDestroy
🗺️ Diagrama: el orden de los hooks
Antes de ver el código, el mapa completo de por dónde pasa un componente desde que nace hasta que se destruye — ngOnChanges solo aparece si el componente tiene @Input() (no es el caso de saludo, sí el de bienvenida más abajo):

constructor()corre primero y no es un hook de Angular (es de JS/TS puro) — todavía no hay acceso a los@Input().ngOnChanges()solo se dispara si el componente tiene algún@Input(): la primera vez corre antes dengOnInit(), y se repite en cada cambio posterior (la flecha punteada que vuelve hacia atrás en el diagrama).ngOnInit()corre una sola vez, ya con los@Input()disponibles.ngOnDestroy()corre justo antes de que Angular quite el componente de la pantalla (p. ej. al cambiar de ruta o al ocultarlo con@if).
ts
import { Component, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-saludo',
imports: [],
templateUrl: './saludo.html',
styleUrl: './saludo.css',
})
export class Saludo implements OnInit, OnDestroy {
constructor() {
console.log('1. Naci (contructor)');
}
ngOnInit(): void {
console.log('2. Ya estoy listo (ngOnInit)');
}
ngOnDestroy(): void {
console.log('3. Ya estoy listo (ngOnInit)'); // 📝 ver nota de abajo, etiqueta errada
}
}📝 Erratas en la captura:
Naci(contructor)→ debería serNací (constructor)(falta tilde y le falta unasa "constructor"; se deja tal cual porque así quedó en el código real). El mensaje dengOnInit()ya se corrigió a "2. Ya estoy listo (ngOnInit)" (ya no repite el texto del constructor). Pero el dengOnDestroy()quedó con la misma etiqueta(ngOnInit)en vez de(ngOnDestroy)— de nuevo pinta a copy-paste sin terminar de editar. Pendiente ver si se corrige.
OnInityOnDestroyson interfaces de@angular/core: cada una obliga a implementar un método con un nombre exacto (ngOnInit()yngOnDestroy()) si la clase diceimplementsesa interfaz. Son 2 de los hooks del ciclo de vida — funciones que Angular llama automáticamente en momentos concretos de la vida del componente.- El
constructor()NO es un hook de Angular — es de JavaScript/TypeScript puro: se ejecuta al crear el objeto (new Saludo()), antes de que Angular haga nada con el componente (antes de leer sus@Input, antes de pintar el HTML). Por eso el profe lo numera como el paso 1 ("Nací") en la línea de tiempo del ciclo de vida, aunque técnicamente no sea parte de la interfazOnInit/OnDestroy. - El mensaje
console.lognumerado ('1. Nací (constructor)') es la pista de que el profe va a ir numerando cada paso del ciclo de vida conforme aparezcanngOnInit()(¿"2. Crecí"?) yngOnDestroy()(¿"Morí"?) — pendiente confirmar los textos exactos en la próxima captura.
🧪 Tip de entrevista: ¿
constructor()es un hook del ciclo de vida de Angular? → No. Es el constructor de la clase (JS/TS), se ejecuta antes que cualquier hook de Angular. El primer hook real del ciclo de vida esngOnInit().
🔌 app.html: se usan los componentes saludo y bienvenida
html
<h3>Hola</h3>
<app-saludo></app-saludo>
<app-bienvenida></app-bienvenida><app-saludo>y<app-bienvenida>son los selectores declarados en cada@Component. Para que compile,SaludoyBienvenidadeben estar en el arrayimports: [...]deApp(app.ts) — son componentes standalone, se importan como cualquier otro, no hace falta unNgModule.- En cuanto aparecen en pantalla, Angular crea cada componente → dispara la secuencia de
console.logde su ciclo de vida en la consola del navegador.
🧪 Tip de entrevista: ¿Para qué sirve
implements OnInitsi igual podría escribirngOnInit()sin la interfaz? → Angular lo llama igual sin la interfaz (por convención de nombre), peroimplementsle pide a TypeScript que verifique en compilación que el método existe y tiene la firma correcta. Es una red de seguridad, no un requisito en tiempo de ejecución.
🙋 Segundo componente: bienvenida (uso real de ngOnInit + @Input + ngOnChanges)
Mientras saludo demuestra el orden de los hooks (constructor → ngOnInit → ngOnDestroy), bienvenida muestra para qué sirve ngOnInit en la práctica, y suma un tercer hook: ngOnChanges, que se dispara cuando cambia un @Input().
ts
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-bienvenida',
imports: [],
templateUrl: './bienvenida.html',
styleUrl: './bienvenida.css',
})
export class Bienvenida implements OnChanges, OnInit {
mensaje = '';
@Input() nombre = '';
constructor() {
console.log('naci');
}
ngOnChanges(changes: SimpleChanges): void {
console.log('entoy en ngOnChanges');
console.log('el nombre cambio a :', changes['nombre'].currentValue);
}
ngOnInit(): void {
this.mensaje = 'Bienvenido al curso';
console.log('entre al ngOnInit');
}
}html
<p>{{ mensaje }}</p>
<p>{{ nombre }}</p>@Input() nombre = ''recibe un dato del componente padre (App), por eso necesitaimport { ... Input ... }— errata típica al escribir esto rápido: olvidar agregarInputal import y quedarse con el error "Cannot find name 'Input'".ngOnChanges(changes: SimpleChanges)se dispara cada vez que Angular detecta un cambio en un@Input()— incluida la primera vez que llega su valor inicial.changes['nombre'].currentValueda el valor nuevo (ychanges['nombre'].previousValueel anterior, aunque no se usó aquí).mensaje = ''arranca vacío (a propósito, para notar el cambio);ngOnInit()recién ahí le pone el valor real ('Bienvenido al curso').
🔢 Orden real confirmado en la consola del navegador
naci
entoy en ngOnChanges
el nombre cambio a : <valor>
entre al ngOnInitconstructor → ngOnChanges → ngOnInit. Esto confirma la regla de Angular: si el componente tiene @Input(), ngOnChanges se ejecuta antes que ngOnInit (Angular necesita entregar los @Input antes de avisar "ya terminé de inicializar"). Si el componente no tuviera ningún @Input, ngOnChanges nunca se llamaría (como pasa con saludo, que no recibe datos del padre).
🧪 Tip de entrevista: ¿En qué orden se ejecutan
ngOnChangesyngOnInit? →ngOnChangessiempre antes, y solo si el componente declara al menos un@Input().ngOnInitse ejecuta una sola vez;ngOnChangesse repite cada vez que cambia algún@Input(incluida la primera entrega de su valor).
🔗 app.ts / app.html: el @Input en acción (padre → hijo)
ts
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Saludo } from './component/saludo/saludo';
import { Bienvenida } from './component/bienvenida/bienvenida';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
imports: [RouterOutlet, Saludo, Bienvenida, FormsModule],
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {
protected readonly title = signal('app-semana04');
nombreEscrito = '';
}html
<input [(ngModel)]="nombreEscrito" placeholder="Ingrese su nombre" />
<app-bienvenida [nombre]="nombreEscrito"></app-bienvenida>nombreEscritoes una propiedad normal (no unsignal()):[(ngModel)]necesita poder asignarle un valor directo con=, y un signal no se actualiza así (se actualiza con.set()), así que mezclarlos rompe el two-way binding.[(ngModel)]="nombreEscrito"(Two-way Binding) actualizanombreEscritoen cada tecla escrita en el<input>.[nombre]="nombreEscrito"(property binding, un solo sentido) le pasa ese valor al@Input() nombredeBienvenida. Cada vez quenombreEscritocambia, Angular vuelve a dispararngOnChangesenBienvenida— de ahí que escribir en el input dispare elconsole.logdengOnChangesuna vez por cada letra.
⚠️ Erratas que salieron al armar esto: una comilla de más en
[nombre] = "nombreEscrito""(rompía el parseo del HTML — errorNG5002), y usarsignal('')junto con[(ngModel)](no compatibles entre sí). Ambas corregidas arriba.
▶️ Cómo correrlo (ya armado en este repo)
Se creó el proyecto real en 02-Ejercicios/app-semana04/ (mismo patrón que las clases anteriores), con los componentes saludo y bienvenida ya cableados en app.html (también viven ahí envio e informacion, de la segunda parte de la clase → ver eventos-en-angular.md):
$ cd 02-Ejercicios/app-semana04
$ npm install // solo la primera vez
$ npm start // = ng serve → http://localhost:4200/Abre esa URL, abre la consola del navegador (DevTools → Console) y ahí se ven los console.log numerados del ciclo de vida de saludo y el de bienvenida.
🏋️ Ejercicios con solución
Ejercicio 1 — corregir la etiqueta de ngOnDestroy() en saludo.ts
El código real de saludo.ts (visto más arriba) tiene una errata ya anotada con 📝: el console.log de ngOnDestroy() quedó con el mismo texto (ngOnInit) en vez de (ngOnDestroy), como si fuera un copy-paste sin terminar de editar. Corrígela sin tocar el número de paso ('3. ...').
Ver solución
ts
import { Component, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-saludo',
imports: [],
templateUrl: './saludo.html',
styleUrl: './saludo.css',
})
export class Saludo implements OnInit, OnDestroy {
constructor() {
console.log('1. Naci (contructor)');
}
ngOnInit(): void {
console.log('2. Ya estoy listo (ngOnInit)');
}
ngOnDestroy(): void {
console.log('3. Ya me destrui (ngOnDestroy)'); // 👈 etiqueta corregida
}
}Ejercicio 2 — agregar @Input() + ngOnChanges a saludo (mismo tratamiento que bienvenida)
saludo hoy solo implementa OnInit y OnDestroy, por eso nunca se dispara ngOnChanges (no tiene ningún @Input()). Dale a Saludo un @Input() nombre = '' y un ngOnChanges con console.log numerado, siguiendo el patrón de bienvenida.ts. Pista: al sumar un hook nuevo entre el constructor y ngOnInit(), hay que renumerar los pasos siguientes.
Ver solución
ts
import { Component, Input, OnChanges, OnInit, OnDestroy, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-saludo',
imports: [],
templateUrl: './saludo.html',
styleUrl: './saludo.css',
})
export class Saludo implements OnChanges, OnInit, OnDestroy {
@Input() nombre = '';
constructor() {
console.log('1. Naci (contructor)');
}
ngOnChanges(changes: SimpleChanges): void {
console.log('2. Cambio el @Input (ngOnChanges):', changes['nombre'].currentValue);
}
ngOnInit(): void {
console.log('3. Ya estoy listo (ngOnInit)');
}
ngOnDestroy(): void {
console.log('4. Ya me destrui (ngOnDestroy)');
}
}Para que ngOnChanges realmente se dispare, app.html tiene que pasarle un valor por property binding, igual que con bienvenida:
html
<app-saludo [nombre]="nombreEscrito"></app-saludo>❓ Preguntas y respuestas
1. ¿constructor() es un hook del ciclo de vida de Angular?
No. Es el constructor de la clase (JS/TS puro) y corre antes que cualquier hook de Angular — todavía no hay acceso a los
@Input(). El primer hook real del ciclo de vida esngOnInit().
2. ¿Para qué sirve implements OnInit si igual podría escribir ngOnInit() sin la interfaz?
Angular lo llama igual (por convención de nombre), pero
implementsle pide a TypeScript que verifique en compilación que el método existe con la firma correcta. Es una red de seguridad, no un requisito en tiempo de ejecución.
3. ¿En qué orden se ejecutan ngOnChanges y ngOnInit?
ngOnChangessiempre antes, y solo si el componente declara al menos un@Input().ngOnInitse ejecuta una sola vez;ngOnChangesse repite cada vez que cambia algún@Input(incluida la primera entrega de su valor).
4. ¿Cuándo se dispara ngOnChanges por primera vez?
En cuanto Angular entrega el valor inicial del
@Input(), incluso si ese valor no "cambió" desde ningún estado anterior (el componente recién se está creando). Por eso enbienvenidaya aparecengOnChangesen el primer render, antes de escribir nada en el<input>.
❓ Pendiente para completar esta clase
- [ ] Ver el resto de la frase en
saludo.html. - [x] Cuerpo real de
constructor(),ngOnInit()yngOnDestroy()— los 3 ya tienen suconsole.lognumerado (1, 2, 3). - [ ] Confirmar si se corrige la etiqueta
(ngOnInit)repetida en elconsole.logdengOnDestroy(). - [x]
ngOnChangesconfirmado enbienvenida(se agregó@Input() nombre+ un<input>con[(ngModel)]enApppara dispararlo). Orden real: constructor → ngOnChanges → ngOnInit. - [ ] Confirmar si se ven más hooks (
ngAfterViewInit,ngDoCheck, etc.). - [ ] Ver cómo/cuándo se dispara
ngOnDestroy()en la demo (normalmente al quitar el componente de la pantalla, p. ej. con@ifo cambiando de ruta). - [x] Agregar el concepto "Ciclo de vida de un componente" a
02-Conceptos.md. - [x] Armar el proyecto real en
02-Ejercicios/app-semana04/de este repo.
📎 Apuntes relacionados
05-Estructura-proyecto.md— nomenclatura moderna de archivos de componente.02-Conceptos.md(sección Ciclo de vida de un componente) — tabla resumen de los hooks y el porqué de usarngOnIniten vez delconstructor.
➡️ Siguiente
Seguir viendo el código del ciclo de vida (posible corrección del console.log de ngOnDestroy(), y si aparecen más hooks) a medida que el profe lo explique.