Introducción a Angular y Fundamentos

1

Desarrollo de Aplicaciones Modernas con Angular

2

Creando tu primer proyecto en Angular

3

Creación de componentes y rutas en Angular para gestión de tareas

4

String Interpolation en Angular: Transmisión de Datos a Templates

5

Modificación de Propiedades HTML con Angular: Property Binding

6

Eventos en Angular: click, doble click y cambio de input

7

Eventos de Teclado en JavaScript: Uso de KeyDown y Change

8

Reactividad Granular con Signals en Angular

9

Reactividad en HTML: Implementación de Signals en JavaScript

Estructuras de control en Angular

10

Estructuras de Control y Renderizado Dinámico en Angular

11

Gestión Dinámica de Tareas en JavaScript: Creación y Eliminación

12

Estructuración de Tareas en JavaScript: Uso de Interfaces y Typing

13

Actualización de tareas con Angular y programación inmutable

14

Uso de ngIf y ngFor para control de flujos en Angular

15

Pluralización y ngSwitch en Angular para manejo de condiciones múltiples

16

Manejo Avanzado de Formularios Reactivos en Angular

17

Validaciones de Inputs en Formularios Reactivos con Angular

Alistando tu aplicación para producción

18

Validación de formularios y estilos dinámicos en Angular

19

Activación dinámica de clases en Angular con validaciones

20

Edición de Tareas Dinámicas en Aplicaciones Web

21

Estados Computados en Programación Reactiva con Signals

22

Persistencia de Tareas con LocalStorage en Angular

23

Compilación y Optimización de Aplicaciones Angular para Producción

24

Lanzamiento de Aplicaciones con Fiverr Hosting y Angular

25

Nuevas Sintaxis Declarativas en Angular Inspiradas en Svelte

26

Sintaxis y rendimiento de for y switch en JavaScript moderno

27

Migración Automática a Nueva Sintaxis de Angular

Componentes Reutilizables y Comunicación

28

Creación de Tienda en Línea con Angular y Tailwind CSS

29

Creación y Organización de Componentes en Angular

30

Componentes Reutilizables en Angular: Creación y Maquetado de Productos

31

Uso de Angular DevTools para Debugging en Angular

32

Inputs en Angular: Comunicación de Datos entre Componentes

33

Outputs en Angular: Comunicación del Hijo al Padre

34

Maquetación de Galería de Productos con Tailwind CSS

Ciclo de vida de los componentes

35

Ciclo de Vida de Componentes en Angular

36

Creación y Uso de Componentes en Angular: Ciclo de Vida y Comunicación

37

Ciclo de Vida de Componentes en Angular: Eventos Clave

38

Detección de Cambios en Inputs con ngOnChange en Angular

39

Prevención de fugas de memoria con ngOnDestroy en Angular

40

Creación de un Reproductor de Audio con WaveSurfer.js en Angular

41

Mejoras de la página About en e-commerce: audio y contador dinámico

Mejorando la interfaz del producto

42

Componentización y Manejo de Datos en Angular

43

Creación de Headers en Páginas Web con Angular

44

Implementación de Interfaz Gráfica para Carrito de Compras Interactivo

45

Implementación de Reactividad en Carrito de Compras con Angular

46

Gestión de Carrito de Compras en Angular con ngOnChanges

47

Gestión de Estado en Angular: Solución al InputDrilling

48

Gestión de Estado Global en Angular con Servicios y Señales

49

Inyección de Dependencias en Angular: Conceptos y Prácticas

Integración y Datos

50

Conexión de Angular a REST API usando Fake Store API de Platzi

51

Creación de Alias para Imports Cortos en Proyectos JavaScript

52

Transformación de Datos con Pipes en Angular

53

Creación y uso de pipes personalizados en Angular

54

Manipulación de Fechas en Angular con Date Functions

55

Manipulación del DOM con Directivas en Angular

56

Deployment de Aplicaciones Angular en Vercel

Enrutamiento y Navegación

57

Creación de Página 404 en Angular: Manejo de Rutas No Encontradas

58

Implementación de Router Link en Angular para SPA eficiente

59

Implementación de Layouts Compartidos en Angular para Vistas Anidadas

60

Uso de RouterLinkActive en Angular para Navegación Activa

61

Routing en Angular: Creación de Páginas de Detalle de Producto

62

Consulta dinámica de detalles de producto con Angular y REST API

63

Galería de Productos Dinámica con Angular y TypeScript

64

Mejoras en Detalle de Producto: Precio, Carrito e Imagen Activa

Perfeccionando tu e-commerce

65

Filtrado de Productos por Categoría en E-commerce

66

Filtros de Productos con Query Params en Angular

67

Optimización de Aplicaciones con Lazy Loading y Code Splitting

68

Optimización de JavaScript en Angular con Lazy Loading

69

Optimización de Carga de Chunks con Preloading en Angular

70

Migración de Angular a Nueva Sintaxis con ng generate

71

Despliegue de Aplicaciones con Verzal en Entornos Productivos

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Persistencia de Tareas con LocalStorage en Angular

22/71
Recursos

¿Cómo solucionamos la reactividad de los botones y el counter?

Para ajustar la reactividad en el counter y asegurar que el botón correspondiente esté activo según el filtro seleccionado, necesitamos realizar algunos cambios en nuestro código. Inicialmente, el counter debe reflejar el estado de las tareas mostradas al usuario. En lugar de leer el array de tareas, ahora leemos del estado derivado, tagsByFilter, que contiene el estado de las tareas bajo el filtro activo.

Además, resulta esencial que el botón apropiado se active de acuerdo con el filtro seleccionado. Utilizaremos clases dinámicas con el property binding para activar la clase selected cuando el filtro coincida con el valor esperado. Esto se puede lograr suscribiéndose al filtro y haciendo una validación de igualdad. Gracias a la tipificación, el filtro solo puede estar en uno de tres estados: all, pending o completed, lo cual nos brinda una validación automática de los estados permitidos, evitando errores por typos.

¿Cómo implementamos persistencia usando localStorage?

La persistencia de los datos es crítica en cualquier aplicación para evitar la pérdida de información tras un refresco de página. Vamos a implementar persistencia en nuestra app de tareas usando localStorage, un método de almacenamiento pequeño del navegador:

  1. Efectos para guardar cambios: Utilizamos effects, una característica de Angular, para guardar automáticamente cada cambio de estado en localStorage. Se debe instanciar un effect dentro de un constructor que monitoriza los cambios en las tareas y las guarda en localStorage.

  2. Serialización: Convertimos el array de tareas en una cadena JSON usando JSON.stringify() antes de almacenarlo para garantizar que localStorage pueda manejarlo correctamente.

  3. Almacenamiento y carga de datos: Al iniciar la aplicación, verificamos si ya existen datos en localStorage. De ser así, estos se cargan como estado inicial, deserializándolos nuevamente en un objeto JavaScript utilizando JSON.parse().

¿Cómo manejamos la inicialización con ngOnInit?

Durante la inicialización del componente, utilizamos ngOnInit para cargar las tareas desde localStorage:

  1. Verificación de datos: Antes de inicializar el estado de las tareas, comprobamos si localStorage tiene datos almacenados. Si existen, los convertimos de texto a objetos y los utilizamos como estado inicial.

  2. Configuración del environment: Eliminamos los estados predeterminados del código y dejamos un array vacío para que se pueble con los datos de localStorage. Esto ayuda a mantener la flexibilidad de la aplicación.

¿Cómo evitamos el borrado accidental de datos?

Un aspecto importante al usar effects es prevenir el borrado accidental de datos debido a que effect se ejecuta al inicio del estado:

  • Configuración posinicialización: Para evitar la limpieza del storage cada vez que se inicia la app, configuramos nuestros effects para que se activen después de que el almacenamiento ya se haya leído e inicializado durante el ciclo de vida del componente.

  • Uso de injectores: Al mover el effect fuera del constructor, empleamos injectors para garantizar que el effect tenga el contexto necesario para funcionar correctamente.

Con estas estrategias, garantizamos que nuestros datos permanezcan persistentes a través de diferentes sesiones del navegador y que se reactive el estado correcto cuando sea necesario. Así, nuestra aplicación de gestión de tareas no solo es reactiva y sensible al contexto del usuario, sino que también proporciona una experiencia de usuario sólida y consistente. ¡Sigue explorando Angular y sus amplias capacidades para hacer aplicaciones web increíbles!

Aportes 16

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

mejor el footer así: \<footer class="footer" \*ngIf="tasks().length > 0"> esto evita que desaparazca cuanto tengo cero completados y de casualidad voy al filtro de completados..
Para entender la inyección de dependencias tengan en cuenta que en angular una dependencia es un servicio, un valor, una función o cualquier parte lógica que una clase/componente necesita para funcionar correctamente. * Por defecto, debemos instanciar o declarar estas dependencias en el constructor. En el ciclo de vida de angular primero se ejecuta el constructor y después el onInit. Por lo tanto el effect guarda el valor inicial de las tareas que es un array vacío. * Para cambiar el orden de renderizado de una dependecia angular tiene los inject que te da la libertad de inyectar/agregar una funcionalidad a tu código en algún punto que tu desees. * Para hacer traking de un estado angular requiere que declaremos el effect en el constructor de esta manera estará escuchando todo el tiempo que el componente este activo el estado de esa variable. * Si no lo inyectamos en el constructor el effect no estará en modo de escucha y no puede detectar cambios en la variable objetivo. * Para declararlo fuera del constructor debemos inyectar todas las dependencias que requiera al momento de declararlo, eso es lo que indica este parámetro  {injector: this.injector} * De manera breve lo que se hace es crear el effect fuera del constructor y después agregarle todas las librerías, lógica y demás al memento de crearlo para que funcione de manera adecuada. * Esta librería esta en construcción supongo que la van a modificar para hacerla mas entendible en proximas versiones, algo mas parecida al useEffect de react que es mas simple de útilizar y cumple la misma función.
a mi me funciona bien sin necesidad del inyector, por favor ¿alguién que me pueda decir el por que? ```js ngOnInit() { const storageTasks = localStorage.getItem('tasks'); if(storageTasks) { const tasks: Task[] = JSON.parse(storageTasks); this.tasks.set(tasks); } } constructor() { effect(() => { localStorage.setItem('tasks', JSON.stringify(this.tasks())); }); } ```ngOnInit() { const storageTasks = localStorage.getItem('tasks'); if(storageTasks) { const tasks: Task\[] = JSON.parse(storageTasks); this.tasks.set(tasks); } } constructor() { effect(() => { localStorage.setItem('tasks', JSON.stringify(this.tasks())); }); }
mi constructor estaba justo despues del inicio de la clase.. y nunca tuve problemas ... no me tocó hacer nada mas para que no se borraran los datos
De esta manera no toco agregar nada mas: `  constructor() {    effect(() => {      localStorage.setItem('tasks', JSON.stringify(this.tasks()))    })  }` `  ngOnInit() {    const storage = localStorage.getItem('tasks');    if (storage) {      this.tasks.set(JSON.parse(storage))    }  }`

Fuente: ChatGPT.
En Angular 17, usar { injector: this.injector } en la función effect asegura que el efecto tenga el contexto correcto del inyector de dependencias del componente. Esto es necesario para resolver correctamente las dependencias y servicios usados en el efecto, y para gestionar de manera adecuada el ámbito y ciclo de vida del efecto en relación con el componente. Es una práctica recomendada para mantener la integridad y coherencia del manejo de estados y dependencias en el componente.

No entiendo bien que hacen estas lineas. injector = inject(Injector) {injector: this.injector}
a mi me funciona bien sin necesidad del inyector, por favor ¿alguién que me pueda decir el por que? ```js export class HomeComponent implements OnInit { ngOnInit() { const storageTasks = localStorage.getItem('tasks'); if(storageTasks) { const tasks: Task[] = JSON.parse(storageTasks); this.tasks.set(tasks); } } constructor() { effect(() => { localStorage.setItem('tasks', JSON.stringify(this.tasks())); }); } ```export class HomeComponent implements OnInit { ngOnInit() { const storageTasks = localStorage.getItem('tasks'); if(storageTasks) { const tasks: Task\[] = JSON.parse(storageTasks); this.tasks.set(tasks); } } constructor() { effect(() => { localStorage.setItem('tasks', JSON.stringify(this.tasks())); }); }
DeepSeek: **Flujo de Ejecución Detallado** Conclusion El `effect` **no se dispara** con el valor inicial `[]` porque: 1. **Fase de Instanciación (Constructor)**: * El `constructor` se ejecuta primero. * Angular crea las **señales** (`tasks` y `filter`) con sus valores iniciales (`[]` y `TaskFilter.All`). * El `effect` se registra, pero **no se ejecuta inmediatamente**. 2. **Inicialización con** `ngOnInit`: * Angular llama a `ngOnInit` después de crear el componente y procesar inputs. * Aquí recuperas los datos de `localStorage` y actualizas `this.tasks.set(...)`. 3. **Primera Ejecución del** `effect`: * El `effect` se ejecuta **después de** `ngOnInit` y la primera detección de cambios. * En este momento, `tasks` ya tiene el valor cargado desde `localStorage`. 1. Angular programa los efectos para después de la inicialización completa. 2. `ngOnInit` actualiza el valor de `tasks` antes de que el efecto se ejecute por primera vez.
```js tasks = signal<Tareas[]>([]); constructor(){ effect(()=>{ const tasks = this.tasks(); localStorage.setItem('tasks', JSON.stringify(tasks)); }); } ngOnInit(){ const tasks = localStorage.getItem('tasks'); if (tasks){ this.tasks.set(JSON.parse(tasks)); } } ```A mí me funciona perfectamente con el código, así sin necesidad de agregar nada más.
tampoco tuve necesidad de usar el inject
Tuve un error en consola: "Error parsing tasks from localStorage: SyntaxError: "undefined" is not valid JSON at JSON.parse (\<anonymous>)" Así es como lo solucioné: ```ts ngOnInit() { const storage = localStorage.getItem('tasks'); if (storage && storage !== 'undefined') { try { const tasks = JSON.parse(storage); this.tasks.set(tasks); } catch (error) { console.error('Error parsing tasks from localStorage:', error); } } this.trackTasks(); } ```
Por alguna razón no me funcionaba el ngOnInit() cuando recargaba la página, solo cuando corrría el npm run start. Lo Solucione quitando el cache en la ventana de networking ![](https://static.platzi.com/media/user_upload/image-247deb57-f4d7-46c6-a210-95806fb649f4.jpg) Tambien no me funcionaba el código del profe porque empezaba con un localstorage que me entregaba null. Lo resolví de la siguiente manera `injector = inject(Injector);` `  ngOnInit() {    console.log('ng om init');` `    const storage = localStorage.getItem('tasks');    console.log(storage)    if ( storage !== null ) {      const tasks = JSON.parse(storage);      this.tasks.set(tasks);    }    this.trackTasks();  }` `  trackTasks() {    console.log("Setting up trackTasks effect");    effect(() => {      const tasks = this.tasks();      console.log(tasks);      localStorage.setItem('tasks', JSON.stringify(tasks));      console.log(localStorage.getItem('tasks'));` `    }, { injector: this.injector });  }` ![](https://static.platzi.com/media/user_upload/image-3ace170f-0f88-4e89-9077-51dd410d7888.jpg)

Opté por colocar la lógica de ‘effect’ en el constructor en lugar de ngOnInit porque ‘effect’ es una función sincrónica. En Angular, el constructor es un buen lugar para inicializar propiedades y ejecutar lógica que no depende de servicios externos o datos asíncronos. Además, reservo ngOnInit para actividades que involucran operaciones asíncronas o dependencias externas.

constructor() {
  const storage = window.localStorage.getItem('tasks');
  if (storage) {
    const tasks = JSON.parse(storage);
    this.tasks.set(tasks);
  }
  this.updateLocalStorage();
}

Créditos a ChatGPT 🤖

Muy buen curso. Gracias profesor Nicolás. ngrx/store es un tema que me ha costado dominar. Esto del efect es similar a ngrx/store? Puede reemplazr a ngrx/store? O definitivamente es lo mismo pero en menor escala? Agradezco sus aclaraciones.