¿Cómo solucionar el problema de prop-drilling en Angular?
El desafío del prop-drilling puede ser un verdadero dolor de cabeza en el desarrollo de aplicaciones Angular. Para solucionarlo, puedes implementar un servicio especializado que actúe como una especie de "store", permitiendo el intercambio de estado entre componentes sin tener que pasarlo desde el padre al hijo repetidamente. Este método simplifica enormemente la gestión de datos en tu aplicación.
¿Cómo crear un servicio en Angular?
El primer paso para abordar el problema de prop-drilling es crear un servicio que centralice la lógica del negocio y gestione los datos. Para esto, se utiliza Angular CLI con un comando específico. A continuación, te mostramos cómo hacerlo:
ng generate service domains/shareit/services/cart
Este comando generará un servicio llamado cart.service.ts. Los servicios son fundamentales para manejar la lógica del negocio y manipular datos dentro de tu aplicación Angular. A diferencia de los componentes, estos no están destinados a renderizar información en la interfaz de usuario.
¿Qué función cumple el servicio en el carrito de compras?
El servicio CartService que has creado será responsable de manejar el estado del carrito de compras. Se encargará de:
Administrar los productos que se añaden al carrito.
Mantener un estado centralizado y notificar a los distintos componentes sobre los cambios de estado.
Utilizar señales (signals) para crear reactividad en Angular.
¿Cómo se administra el estado con señales?
Las señales se pueden usar dentro de los servicios para asegurarse de que los cambios de estado se propaguen correctamente. Aquí tienes un ejemplo de cómo definir un array de productos en el carrito como una señal vacía al inicio:
products:Signal<Product[]>=Signal(()=>[]);
¿Cómo añadir productos al carrito?
Para agregar un producto al carrito, se emplea el método addProduct() dentro del servicio que actualiza el estado utilizando las capacidades reactivas de Angular.
Con el servicio listo, el siguiente paso es inyectarlo en los componentes que necesiten acceder a los datos del carrito sin utilizar inputs innecesariamente. Aquí te mostramos cómo realizar la inyección de dependencias:
Al hacer esto, el componente podrá comunicarse con el servicio CartService para obtener el estado del carrito y la lista de productos sin haber pasado esa información como prop desde un componente padre.
¿Qué beneficios tiene este enfoque?
Desacoplamiento: Los componentes se centran en la visualización, mientras que el servicio se encarga de la gestión de datos.
Reutilización: Varios componentes pueden compartir un mismo servicio.
Escalabilidad: Facilita la administración de aplicaciones de gran tamaño.
Al adoptar estos métodos, se simplifica significativamente la arquitectura de tu aplicación Angular, se minimizan complicaciones asociadas al prop-drilling, y se mejora la eficiencia general. Este enfoque modular es clave para desarrollar aplicaciones robustas y escalables. ¡Sigue explorando y aplicando estos conceptos para mejorar continuamente tus habilidades en Angular!
sí, eso estaba viendo yo, antes lo utilizaba, pero con esto está genial
Correcto, realmente los action y los reducer que definiamos en NgRx o Redux en React, aca son simples métodos de la clase Service y con el computed se pueden vincular aquellos subestados que dependen de otro. Muy potente.
Creo que esto le va a dar durísimo a React.
Tal vez en las siguientes clases lo aclaren, pero existe diferencia de como antes se inyectaban dependecias en el constructor a como lo hace en esta clase?
Antes era:
contructor(private cartService: CartService) {}
y ahora:
private cartService = inject(CartService)
Creo que es lo mismo que se hace en Spring, se usa la anotacion @Autowired para que Spring se encarge de inyectar la dependencia sin pasarlo como parametro al construir el objeto. En este caso, se usa inject() para que Angular lo inyecte.
En pocas palabras, la unica diferencia es que con inject dejamos que Angular inyecte la dependencia por nosotros.
En Angular 16 los docs decían que los signals se declaraban con readonly por ejemplo:
readonly requestId = signal<number>(0)
¿Ya no se usa eso con Angular 17?
¡Hola! Lo puedes seguir haciendo, pero no es obligatorio, esto sería más una buena práctica para no rescribir un signal a otra cosa.
Y con esto, ya podríamos colocar el header en el primer nivel y poder compartir el array de carritos en el servicio
Si tanto el header como el footer se mantiene igual a través de las rutas, entonces debes colocarlo en el mismo nivel que el outlet 😉
Tengo una duda con el signals en los servicio, cree un servicio que alimenta 3 compoentes el padre y dos hijos, pero si quiero lees la informacion del servicio o el computed desde un hijo me aparece vacio y para que en el padre me muestre la informacion que se insertar en los signal desde los hijos si no mando a insrtar algo desde el padre al inicial la pantalla no funciona no muestra la actualizacion asi que no estoy claro del tema sabel algo respecto alguien?
private cartService = inject(CartService);
🔸 private hace que la variable solo sea accesible dentro del componente (TypeScript), pero no en la vista (HTML).
🔸 Si intentaras usarlo en el HTML, Angular no lo encontraría y mostraría un error de propiedad desconocida.
🔸 Para que funcione en el template, tendrías que cambiarlo a public manualmente:
public cartService = inject(CartService);
Esto lo haría accesible, pero la inyección en el constructor sigue siendo más clara en estos casos.
simpicidad a comparacion con react
Una pregunta actualmente tengo un select en un componente header que al cambiar la selección emite un output para desde el padre (página) ejecutar una llamada a una u otra api rest, como puedo realizar esta acción sin hacerlo mediante un output y aplicar signal ya que tengo problema de prop drilling al llamar en todas las páginas la misma función y la misma acción para poder hacer el change. En resumen no entiendo cómo realizar un change sin realizar un output al componente 😬