Introducción a Angular y Fundamentos
Aprende Angular 17
Creando tu primer proyecto en Angular
Implementando estilos
Mostrando elementos
Property Binding en Angular
Event Binding: click y doble click
Event binding: keydown
Modelo de reactividad con Signals
Creando un Signal en Angular
Estructuras de control en Angular
Directivas de control
Uso de ngFor
ngFor para objetos
Update Tasks
Uso de ngIf
Uso de ngSwitch y ngSwitchDefault
Controlando un input
Manejo de formularios en Angular
Alistando tu aplicación para producción
Estilos al modo Angular
Clases en Angular
Editing mode
Estados compuestos con computed
Usando effect para localStorage
Uso de ngbuild
Despliegue con Firebase Hosting
Nueva sintaxis en Angular
Directivas @For, @switch
Migrando a la nueva sintaxis de Angular v17
Componentes Reutilizables y Comunicación
Construyendo un e-commerce en Angular
Componentes en Angular
Mostrando los componentes
Angular DevTools
Uso de Inputs en Angular
Uso de Outputs en Angular
Componentes para Producto
Ciclo de vida de los componentes
Ciclo de vida de componentes
Ciclo de vida de componentes: ngOnChanges
Ciclo de vida de componentes: ngOnInit
Detectando cambios en los inputs
Evitando memory leaks con ngDestroy
Audio player con ngAfterViewInit
Creando la página "about us" o "conócenos"
Mejorando la interfaz del producto
Creando componente de productos
Creando el Header
Creando el carrito de compras
Comunicación padre e hijo
Calculando el total con ngOnChanges
El problema del prop drilling
Reactividad con signals en servicios
Entendiendo la inyección de dependencias
Integración y Datos
Obteniendo datos una REST API
Importaciones cortas en Typescript
Pipes en Angular
Construyendo tu propio pipe
Utilizando librerías de JavaScript en Angular
Conociendo las directivas
Deployando un proyecto en Vercel
Enrutamiento y Navegación
Ruta 404
Uso del RouterLink
Vistas anidadas
Uso del RouterLinkActive
Detalle de cada producto
Obteniendo datos del producto
Galería de imagenes
Detalle de la galería
Perfeccionando tu e-commerce
Mostrando categorias desde la API
Url Params
LazyLoading y Code Splitting
Aplicando LazyLoading
Prefetching
Usando la nueva sintaxis de Angular 17
Lanzando tu aplicación a producción
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
En el desarrollo de aplicaciones web, comprender cómo manejar correctamente el ciclo de vida de los componentes es crucial, especialmente cuando hablamos de prevenir fugas de memoria. En Angular, el método ngOnDestroy
es nuestra ancla para abordar esto efectivamente. Este método se ejecuta cuando un componente se desmonta, liberando los recursos que el componente estaba utilizando. Vamos a desglosar este concepto con un ejemplo práctico y ver cómo evitar que ciertas tareas sigan consumiendo memoria innecesariamente.
Para comprender mejor el papel de ngOnDestroy
, primero configuraremos un contador simple que actualiza su valor cada segundo. Usaremos setInterval
para incrementar este contador y ver cómo todo encaja en nuestro ciclo de vida del componente.
let counterRef: number | undefined; // Variable para almacenar la referencia del intervalo
// Ejemplo de creación de un contador con setInterval
counterRef = window.setInterval(() => {
this.counter.update(value => value + 1); // Actualizamos el contador
}, 1000);
En este bloque de código, hemos creado un contador que se incrementa cada segundo utilizando setInterval
. Esto es útil para entender cómo JavaScript maneja tareas en segundo plano.
Un error común es asumir que al destruir un componente con ngOnDestroy
, todas las funciones asociadas también se detienen automáticamente. Sin embargo, esto no ocurre de manera predeterminada. En el caso de setInterval
, JavaScript seguirá ejecutándolo en el event loop, porque no se le ha dado una instrucción explícita de detención.
ngOnDestroy() {
if (counterRef !== undefined) {
window.clearInterval(counterRef); // Detenemos el intervalo
}
}
La clave aquí es usar window.clearInterval
en ngOnDestroy
para matar el intervalo. Al hacerlo, prevenimos que el proceso siga consumiendo recursos innecesarios del navegador, eliminando así cualquier riesgo de fugas de memoria.
El principio aplicado a setInterval
se extiende a otras ejecuciones en segundo plano, como WebSockets, Subscripciones a Streams de datos, y más. Si no las detienes adecuadamente, pueden continuar ejecutándose incluso después de que el componente haya sido destruido, resultando en un consumo innecesario de memoria y recursos.
Para cada tipo de tarea en segundo plano:
ngOnDestroy
.unsubscribe
para cancelar subscripciones activas a data streams.Al entender y aplicar estos principios, puedes crear aplicaciones web que no solo funcionan correctamente, sino que también gestionan recursos de manera eficiente, asegurando una experiencia de usuario más fluida y libre de problemas de memoria. La atención a estos detalles es lo que distingue a un desarrollador experto en Angular. ¡Continúa explorando y aprendiendo para convertirte en uno!
Aportes 13
Preguntas 0
Bueno voy a hablar de lo mismo que los comentarios de abajo de como solucionar el error.
El codigo de angular se ejecuta solamente del lado del servidor SSR (Server Side Render) por las configuraciones que deberiamos tener en este momento.
Por lo cual no existen (window y document) hasta que pase la primera renderizacion por eso el codigo funciona aun con el mensaje de error en consola.
Soluciones hay varias pero usar las herramientas que ofrece Angular es muy util esas son Inject, PLATFORM_ID from @angular/core y la validacion de isPlatformBrowser from @angular/common.
export class CounterComponent {
@Input({required: true}) duration: number = 0; // usar el hardtypo no es obligatorio
@Input({required: true}) message: string = '';
counter = signal(0);
counterRef: number | undefined;
constructor( @Inject(PLATFORM_ID) private plataformId: object){ // Never async
console.log('construnctor');
console.log('-'.repeat(10));
// before show component
}
ngAfterViewInit(){
// after render
// handle child changed
console.log('ngAfterViewInit');
console.log('-'.repeat(10));
if (isPlatformBrowser(this.plataformId)) { // Esto se va a ejecutar solamente en el navegador
// podemos usar todo lo que queramos del DOM dentro del if
this.counterRef = window.setInterval(() => {
console.log('run interval');
this.counter.update(statePrev => statePrev + 1);
},1000)
}
}
}
Esto es muy util si queremos asegurarnos de que ese bloque de codigo se va a ejecutar solamente en el navegador.
Creo que se podria hacer un decorador para que sea mas facil de implementar y menos repetitivo.
✅
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?