Introducción a Angular y Fundamentos

1

Aprende Angular 17

2

Creando tu primer proyecto en Angular

3

Implementando estilos

4

Mostrando elementos

5

Property Binding en Angular

6

Event Binding: click y doble click

7

Event binding: keydown

8

Modelo de reactividad con Signals

9

Creando un Signal en Angular

Estructuras de control en Angular

10

Directivas de control

11

Uso de ngFor

12

ngFor para objetos

13

Update Tasks

14

Uso de ngIf

15

Uso de ngSwitch y ngSwitchDefault

16

Controlando un input

17

Manejo de formularios en Angular

Alistando tu aplicación para producción

18

Estilos al modo Angular

19

Clases en Angular

20

Editing mode

21

Estados compuestos con computed

22

Usando effect para localStorage

23

Uso de ngbuild

24

Despliegue con Firebase Hosting

25

Nueva sintaxis en Angular

26

Directivas @For, @switch

27

Migrando a la nueva sintaxis de Angular v17

Componentes Reutilizables y Comunicación

28

Construyendo un e-commerce en Angular

29

Componentes en Angular

30

Mostrando los componentes

31

Angular DevTools

32

Uso de Inputs en Angular

33

Uso de Outputs en Angular

34

Componentes para Producto

Ciclo de vida de los componentes

35

Ciclo de vida de componentes

36

Ciclo de vida de componentes: ngOnChanges

37

Ciclo de vida de componentes: ngOnInit

38

Detectando cambios en los inputs

39

Evitando memory leaks con ngDestroy

40

Audio player con ngAfterViewInit

41

Creando la página "about us" o "conócenos"

Mejorando la interfaz del producto

42

Creando componente de productos

43

Creando el Header

44

Creando el carrito de compras

45

Comunicación padre e hijo

46

Calculando el total con ngOnChanges

47

El problema del prop drilling

48

Reactividad con signals en servicios

49

Entendiendo la inyección de dependencias

Integración y Datos

50

Obteniendo datos una REST API

51

Importaciones cortas en Typescript

52

Pipes en Angular

53

Construyendo tu propio pipe

54

Utilizando librerías de JavaScript en Angular

55

Conociendo las directivas

56

Deployando un proyecto en Vercel

Enrutamiento y Navegación

57

Ruta 404

58

Uso del RouterLink

59

Vistas anidadas

60

Uso del RouterLinkActive

61

Detalle de cada producto

62

Obteniendo datos del producto

63

Galería de imagenes

64

Detalle de la galería

Perfeccionando tu e-commerce

65

Mostrando categorias desde la API

66

Url Params

67

LazyLoading y Code Splitting

68

Aplicando LazyLoading

69

Prefetching

70

Usando la nueva sintaxis de Angular 17

71

Lanzando tu aplicación a producción

You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

0 Días
0 Hrs
44 Min
11 Seg

Calculando el total con ngOnChanges

46/71
Resources

How does the current component architecture work in Angular?

To master Angular, it is critical to understand how components communicate with each other. In this case, the architecture consists of three base nodes, with ListComponent functioning as a master node. This master node manages communication between components through inputs and outputs.

  1. Multi-product rendering: ListComponent uses ng-for to render multiple products.
  2. Communication between components:
    • Whenever a user adds a product to the cart, ProductComponent sends a notification to ListComponent through an output.
    • ListComponent not only maintains a reference to the products in the cart, but also notifies HeaderComponent of changes via input.
    • HeaderComponent updates the shopping cart in the application side menu.

This flow ensures a reactive and efficient interaction between the components. An additional challenge has been to improve the visual appearance and calculate the total of the cart list.

How to improve the visual appearance and calculation of the total?

For a better visual presentation, simple enhancements using flex were used to organize the product image, title and price in a neater and clearer format. However, the final important goal is to show the total of the products in the cart. This can be achieved by following these steps:

How to calculate the total of the products?

By proposing a method inside HeaderComponent called CalculateTotal, you can accumulate the price of all the products in the cart. An efficient way to do this is using the reduce method in JavaScript.

calculateTotal() { return this.cart.reduce((total, product) => total + product.price, 0);}
  • Accumulator: Initialized to zero and updating with the price of each product.
  • Total return: This method returns the accumulated total directly.

Why avoid functions in the template?

Although string interpolation in Angular allows you to call functions directly in the template, doing so can affect the performance of the application, unless the function is a signAll. To avoid performance issues:

  1. Keep processed data in the template: already preprocessed to avoid direct execution.
  2. Use of signals or computed signals: Although card is not a signal and cannot be used directly as computed, it is important to manage how the total is recalculated with new data.

What is a more efficient solution?

Adopt best practices using ngOnChanges to recalculate the total when there are changes in the input:

  1. Create a total variable as a signal:
total = signAll(0);
  1. Using ngOnChanges: Check for changes in the input.
ngOnChanges(changes: SimpleChanges) { if (changes.cart) { this.total.set(this.calculateTotal()); } }}
  • Propagation: Each change in cart recalculates the total.
  • Better performance: Without executing functions in the template directly, being optimized by signals.

With this approach, Angular optimizes component interaction and improves performance by avoiding direct functions inside the template. In addition, encouraging these advanced practices ensures clean, efficient and scalable code. Remember that programming is a vast world and there are always new techniques and strategies to learn, so keep exploring and expanding your skills!

Contributions 21

Questions 2

Sort by:

Want to see more contributions, questions and answers from the community?

Wooow hasta el momento el curso esta excelente. Me encanta que hayan mostrando ejemplos prácticos con ngOnDestroy() ngViewAfterInit(), constructor(), ngOnInit() y ahora ngOnChanges, sin duda el curso esta de lo mejor 💚

La preocupación presentada en el curso es válida. Llamar a funciones directamente desde el template en Angular puede ser una mala práctica si esas funciones realizan cálculos o procesamientos pesados y son llamadas frecuentemente, como cada vez que se ejecuta la detección de cambios de Angular. Esto puede llevar a problemas de rendimiento, especialmente si la función se encuentra en una parte del template que se actualiza con mucha frecuencia. En el código que has compartido, se está utilizando el `ngOnChanges` lifecycle hook para actualizar el valor de `total` cuando cambia el `cart`. Este enfoque es una buena práctica porque `ngOnChanges` se llama solo cuando el valor de una propiedad `@Input()` cambia, lo que significa que `calcTotal()` no se ejecutará en cada ciclo de detección de cambios, sino solo cuando sea necesario. El uso de `ngOnChanges` es una forma de asegurarse de que los cálculos solo se realicen cuando los datos relevantes se actualicen, en lugar de hacerlo cada vez que se detectan cambios, lo que podría ser mucho más frecuente. Para explicar el código que has subido: * `ngOnChanges` es un hook del ciclo de vida de un componente Angular que se activa cuando Angular establece o restablece los datos de propiedades vinculadas a datos. Específicamente, se llama antes de `ngOnInit()` y cada vez que hay cambios en las propiedades de `@Input()` del componente. * `calcTotal()` es un método en tu componente que calcula la suma total de los precios en el carrito. * `this.total.set(this.calcTotal())` actualiza el valor de `total` usando el método `set` de una señal reactiva (probablemente parte de una biblioteca de manejo de estado que estás utilizando). En resumen, estás siguiendo una buena práctica al calcular el total en `ngOnChanges`, ya que esto asegura que la suma solo se recalcula cuando es necesario, y no en cada ciclo de detección de cambios que podría afectar al rendimiento. Esto se alinea con las prácticas recomendadas en Angular para evitar realizar trabajos innecesarios en el template y en los hooks del ciclo de vida.
Estaría bueno que se explicara más a profundidad por qué ejecutar funciones en el template trae concecuencias negativas en el redimiento de la aplicación.
En el caso de que hayas colocado el header en el AppComponent. el diagrama crece aún más pero es lo ideal. Creo yo que debió colocarse allí desde el inicio para asegurar la escalabilidad
Esto es lo que dice ChatGPT respecto a si es mejor implementar el GET o el ngOnChanges para calcular el total del carrito de compras: "Para una aplicación de tamaño moderado a grande, **usar** `ngOnChanges` para actualizar el total cuando el carrito cambia es probablemente la mejor opción en términos de rendimiento y escalabilidad. Para aplicaciones pequeñas o para una implementación rápida, **usar un getter** puede ser suficiente y más sencillo de implementar."
Ademas de lo que menciono Nicolas, se puede hacer una implementación basada en Signals, que a lo mejor no es compatible con la manera antigua de hacer Angular. Si quisieramos usar computed junto con Signals, lo podemos hacer de la siguiente manera: ```ts import { Component, computed, input, signal } from '@angular/core'; export class HeaderComponent { hideSideMenu = signal(true); cart = input.required<Product []>(); total = computed(() => this.cart().reduce((total, {price = 0}) => total + price, 0 )); toggleSideMenu () { this.hideSideMenu.update((prevState) => !prevState); } } ```De esta manera podemos implementar los mismo usando `computed` que permite escribir menos código para conseguir el mismo resultado. ⚠️ **Un detalle importante de esta implementación es que se debe marcar el input como requerido** para no tener problemas de TypeScript con never. De esta manera solo haría falta modificar el template agregando la sintaxis de signal en el ciclo for y al usar el total. ```html
``` Y cambiar el total de esta forma ```html

Total {{total() }}

``` ✅ De esta manera podríamos hacer la implementación con un input con signals. Esto está documentado en la página de Angular [Accepting Data with Input Properties](https://angular.dev/guide/components/inputs)
Así voy: ![](https://static.platzi.com/media/user_upload/image-bab717ac-7562-423e-838a-39ca97cb9968.jpg)![]()
Esta es la solución que plateo, no se si esta bien jeje ![](https://static.platzi.com/media/user_upload/image-d20b8a98-b8d2-4965-8bd2-c1f2f26d4fca.jpg) Y aca el resultado: ![](https://static.platzi.com/media/user_upload/image-b1b22a20-6278-41a4-9298-416ae52a924e.jpg) Cualquier sugerencia será muy bien recibida jeje
Algo que haría para evitar el ngOnChanges, es el `SetInput` <https://dev.to/mezagini/setinput-5gjj> ```js @Input() set changeProduct(cart: Product[]) { this.cart = cart; } ````@Input()` ` set changePerson(newPerson: Person) {` ` this.person = newPerson;` ` }`
Reto: ![](https://static.platzi.com/media/user_upload/upload-686c6621-0805-4dba-a699-5ccc9ab5fc47.png)
Reto: ![]()![]()
Yo si pude calcular el total con computed gracias a que separé el slideBar del header en componentes distintos, ya que me pareció que sileBar ameritaba su propia lógica dado que ahí cree varios métodos como: * Calcular el precio total de la compra * Calcular el total de productos(no repetidos) * Aumentar la catidad de articulos por producto * Disminuir la catidad de articulos por producto * Eliminar productos del carrito * Calcular el total de articulos * Y adicional un componente de UI para indicar que la compra es gratis una vez alcanzados los $2500 pesos `//Calculate the total cart``  totalCart = computed(() => {    ``const`` cart = this.cartService.cart();    ``const`` amount = cart.reduce((sum, cart) => sum + (cart.price * (cart.quantity ?? 1)), 0);    ``return`` amount;  })`
En el anterior video, algunos resolvieron el problema usando un get. Para **cálculos simples y frecuentes**, el **getter** es más conveniente. Para **lógica más compleja o controlada** sobre cambios específicos de los inputs, `ngOnChanges` sería más adecuado. * `ngOnChanges` solo se ejecuta cuando los valores de los `@Input()` cambian * Usar un `getter` es ideal cuando el valor debe recalcularse cada vez que se accede y no necesitas control explícito de los cambios. En algunos sistemas que se deben de realizar calculos contables se tienen en cuanta otras funciones para llegar a obtener el valor total.
Utilice estos estilos y la conversion a moneda de price y total, por si alguien lo necesita ``` \
\
\
\

\

\

{{ product.price | currency:'Bs.':'symbol':'1.2-2' }}\

\
\
{{ product.title }}\

\

{{ product.title }}\

\
\

{{ product.price | currency:'Bs.':'symbol':'1.2-2' }}\

\
\
\

Total:\

\

{{ total() | currency:'Bs.':'symbol':'1.2-2' }}\

\
\
``` ![](https://static.platzi.com/media/user_upload/Screenshot%202024-10-16%20at%204.59.30PM-49626646-6d4e-4efe-87ea-0ded32353454.jpg)
![](https://static.platzi.com/media/user_upload/image-60b4a33d-e073-42e8-80ce-6f81b5112bc0.jpg)\
    \
      \
        \        \

{{product.title}}\

      \
      \

${{product.price}}\

    \
  \
Hey les comparto una experiencia interesante de ngOnChange que nos puede evitar varios bugs en el futuro. Es su funcionamiento cuando usamos datos primitivos vs complejos en Javascript. Verán... cuando utilizamos datos primitivos (strings, numeros, booleanos) que se **pasan por valor**, el funcionamiento es exactamente el que esperamos; cambiamos el valor en el componente padre y se dispara el ngOnChange hook en el componente hijo. Sin embargo, cuando pasamos datos complejos o no primitivos (functions, arrays, objects ) a través de un input y cambiamos sus datos en el padre… algo así: ```js const motoBrands = "['Ducati', 'BMW', 'Honda']" motoBrands[0] = 'Yamaha' motoBrands.push('Suzuki') ``` Estos cambios no dispararán el ngOnChange hook del componente hijo. ¿Por qué sucede esto, si a priori estamos cambiando el objeto? Esto se debe a que ngOnChange está programado para dispararse solo cuando cambie el valor de la referencia en memoria. La siguiente forma de añadir items al carrito asegura un cambio de referencia en memoria, por ende, la ejecución del ngOnChange hook en componentes hijos. ```js addNewItem(item: Product) { const newCart = [...this.shoppingCart.value, item] this.shoppingCart.set(newCart); } ``` Los invito a ver más al respecto en las siguiente clases: <https://platzi.com/new-home/clases/8617-javascript-fundamentos/66439-tipos-de-datos-mutabilidad-e-inmutabilidad/> <https://platzi.com/new-home/clases/8617-javascript-fundamentos/66440-paso-por-valor/> <https://platzi.com/new-home/clases/8617-javascript-fundamentos/66441-paso-por-referencia/> Espero sea de utilidad, happy coding!
Hey les comparto una experiencia interesante de ngOnChange que nos puede evitar varios bugs en el futuro. Se trata de su funcionamiento cuando usamos datos primitivos vs complejos en Javascript. Verán... cuando utilizamos datos primitivos (strings, numeros, booleanos) que se **pasan por valor**, el funcionamiento es exactamente el que esperamos; cambiamos el valor en el componente padre y se dispara el ngOnChange hook en el componente hijo. Sin embargo, cuando pasamos **datos complejos o no primitivos (functions, arrays, objects )** a través de un input y cambiamos sus datos en el padre… algo así: ```js const motoBrands = "['Ducati', 'BMW', 'Honda']" motoBrands[0] = 'Yamaha' motoBrands.push('Suzuki') ```Estos cambios no dispararán el ngOnChange hook del componente hijo. ¿Por qué sucede esto, si a priori estamos cambiando el objeto? Esto se debe a que ngOnChange está programado para dispararse solo cuando cambie el **valor de la referencia en memoria.** La siguiente forma de añadir items al carrito asegura un cambio de referencia en memoria, por ende, la ejecución del ngOnChange hook en componentes hijos. ```js addNewItem(item: Product) { const newCart = [...this.shoppingCart.value, item] this.shoppingCart.set(newCart); } ```Los invito a ver más al respecto en las siguiente clases: <https://platzi.com/new-home/clases/8617-javascript-fundamentos/66439-tipos-de-datos-mutabilidad-e-inmutabilidad/> <https://platzi.com/new-home/clases/8617-javascript-fundamentos/66440-paso-por-valor/> [https://platzi.com/new-home/clases/8617-javascript-fundamentos/66441-paso-por-referencia/ Espero les sea de utilidad, happy coding! ](https://platzi.com/new-home/clases/8617-javascript-fundamentos/66441-paso-por-referencia/)
Mi solución la realice de la siguiente forma, agregando tambien la funcionalida de eliminar el producto } ```js totalProducts(products:Array<Product>){ return products.reduce((totalValue,ProductValue:Product) => totalValue + ProductValue.price,0); } ngOnChanges(changes:SimpleChanges){ try{ if (!Object.hasOwn(changes,"cart")) { throw `No existe "cart" en el compoente Header` } const {cart} = changes this.total.set(this.totalProducts(cart.currentValue)) }catch(error){ console.error(error); } } ```
solución al reto ```js

Products on the cart


@for (cart of cart; track $index) {

Producto: {{cart.title}}

Precio: ${{cart.price}}

}
```

![](https://static.platzi.com/media/user_upload/image-a2037a03-842d-447b-9e42-758641d6e212.jpg) lo hice asi!!