Angular se reinventa: Bienvenido a la era de Angular v18
Tags: angular, typescript
Published: 2024-05-23 22:04:13

La espera ha terminado. Angular v18, la última versión del popular framework de Google para desarrollo web, ha llegado para revolucionar la forma en que creamos aplicaciones web. Con un enfoque claro en la optimización del rendimiento y la introducción de nuevas características que simplifican el desarrollo.

## Change detection - Zoneless (Experimental)

Tradicionalmente, una librería llamada ZoneJS se ha encargado de activar la detección de cambios en Angular. Sin embargo, esta librería ha presentado ciertas desventajas en términos de rendimiento y experiencia de desarrollo. 

Angular v18 introduce una nueva forma de activar la detección de cambios. En lugar de depender de ZoneJS para saber cuándo es posible que algo haya cambiado, Angular ahora puede programar una detección de cambios por sí mismo. Toma en cuenta que esto está en una etapa experiemental.

Para hacerlo, se agregó un nuevo programador al framework (llamado ChangeDetectionScheduler) y este programador se usa internamente para activar la detección de cambios. Este nuevo programador está habilitado de forma predeterminada en v18, incluso si usa ZoneJS. Sin embargo, el objetivo es alejarse progresivamente de ZoneJS y confiar únicamente en este nuevo planificador.

Con este nuevo programador, Angular ya no depende únicamente de ZoneJS para activar la detección de cambios. De hecho, el nuevo programador activa una detección de cambios cuando se activa un host o un escucha de plantilla, cuando se adjunta o elimina una vista, cuando una tubería asíncrona detecta una nueva emisión, cuando se llama al método `markForCheck()`, cuando se establece un valor de señal , etc. Lo hace llamando a `ApplicationRef.tick() `internamente.


### Experimentando con zoneless

Puedes probar el soporte para zoneless en Angular despues de actualizar a la versión 18, solo necesitas hacer un pequeño cambio en el `boostrap` de tu aplicación.

```typescript
bootstrapApplication(App, {
  providers: [
    provideExperimentalZonelessChangeDetection()
  ]
});
```

Al hacerlo, Angular ya no dependerá de ZoneJS para activar la detección de cambios. Así que puedes eliminar ZoneJS de tu aplicación si lo deseas (y si no tienes dependencias que dependan de él, por supuesto). En ese caso, puedes eliminar ZoneJS de los polyfills en tu archivo angular.json.

Si tus componentes son compatibles con la estrategia de detección de cambios `ChangeDetectionStrategy.OnPush` de Angular, también deberían ser en su mayoría compatibles con zoneless, lo que hará que su transición sea perfecta.

De cara al futuro, un Angular sin ZoneJS abre un abanico de 
posibilidades para los desarrolladores:

- **Mayor compatibilidad:** Facilita la creación de micro-frontends y la interoperabilidad con otros frameworks.
- Rendimiento mejorado: Tiempos de renderizado iniciales más rápidos y mejor rendimiento en tiempo de ejecución.
- **Aplicaciones más ligeras:** Tamaños de paquete más pequeños y tiempos de carga de página más rápidos.
- **Depuración simplificada: **Rastros de pila más legibles que facilitan la identificación y resolución de errores.
- **Procesos de depuración más sencillos:** Elimina la complejidad asociada a ZoneJS, haciendo que la depuración sea más intuitiva.

La mejor forma de utilizar zoneless es con componentes que utilicen signals. Por ejemplo:

```typescript
@Component({
  ...
  template: `
    <h1>Hello from {{ name() }}!</h1>
    <button (click)="handleClick()">Go Zoneless</button>
  `,
})
export class App {
  protected name = signal('Angular');

  handleClick() {
    this.name.set('Zoneless Angular');
  }
}
```

En el ejemplo anterior, al hacer clic en el botón se invoca el método `handleClick`, que actualiza el valor del signal y actualiza la interfaz de usuario. Esto funciona de manera similar a una aplicación que usa ZoneJS, con pocas diferencias. Con ZoneJS, Angular ejecutaba la detección de cambios cada vez que el estado de la aplicación podía haber cambiado. Sin zonas, Angular limita esta verificación a menos activadores, como actualizaciones de signals. Este cambio también incluye un nuevo programador con fusión para evitar verificar cambios varias veces consecutivas.

### Async/await nativo en aplicaciones zoneless

ZoneJS tiene una particularidad: no puede funcionar con async/await. Quizás no lo sepas, pero cada vez que usas async/await en tu aplicación, la CLI transforma tu código para usar promesas "normales". Esto se llama reducción de nivel, ya que transforma el código ES2017 (async/await) en código ES2015 (promesas regulares).

Hoy en día, si creas una aplicación que utiliza la detección de cambios sin ZoneJS (experimenta), Angular CLI utilizará el async/await nativo sin bajarlo a promesas. Esto mejorará la depuración y hará que tus paquetes sean más pequeños.


## Signals - Propuesta de Estandarización

Esto no es exactamente nuevo en Angular v18, ya que en la version v17 se anuncio los nuevos signals inputs, signal queries y la nueva sintaxis de input y ouput.

Ahora con la llegada de la de Angular v18 tenemos las Siganal APIs en `developer preview`.

Cada vez los signals se están volviendo mucho más populares, de hecho ya existe una propuesta para estandarizar los signals y que puedan ser agregados a JavaScript. Puedes profundizar en la [propuesta](https://github.com/tc39) o en este interesante [blog post](https://eisenbergeffect.medium.com/a-tc39-proposal-for-signals-f0bedd37a335) para conocer más al respecto.

`new Signal.State()` sería el equivalente de `signal()` en Angular. `new Signal.Computed()` sería el equivalente de `computed()`. No hay equivalentes para el efecto: como todos los frameworks tienen necesidades ligeramente diferentes, esto queda fuera del alcance de la propuesta, y los frameworks pueden implementarlo como mejor les parezca basándose en el nuevo Signal.subtle.Watcher().

Dato curioso: ¡El polyfill de Signal actual en la propuesta se basa en la implementación de Angular!

## Built-in control flow - Ahora es estable

La sintaxis de `Built-in control flow` introducida en Angular v17 ya no es una función en `developer preview` para desarrolladores y se puede utilizar de forma segura. Como ahora es la forma recomendada de escribir plantillas, debería considerar usarla en tus aplicaciones. Puede migrar fácilmente tus aplicaciones utilizando los esquemas proporcionados. Por ejemplo:

```ssh
ng generate @angular/core:control-flow
```

Más información: [Guía oficial](https://angular.dev/guide/templates/control-flow)

## Deferrable views - Ahora es estable

La sintaxis @defer también es estable. @defer te permite definir un bloque de plantilla que se cargará de forma diferida cuando se cumpla una condición (con todos los componentes, canalizaciones, directivas y bibliotecas utilizadas en este bloque también cargados de forma diferida).

Más información: [Guía oficial](https://angular.dev/guide/defer)


## Material v3 - Ahora es estable

Hace unos meses atras Angular introdujo suporte experimental para Material 3, ahora con la llegada de Angular v18 tenemos soporte estable, ahora podemos utilizar los nuevos features de Material 3 en nuestros prouyectos de Angular con Angular Material.

![Angular Material home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cita7ria7u44b96pxnj0.png)


## Mejoreas en el server-side rendering

Con Angular v18 llegaron algunas nuevas características relacionas al Server Side Rendering (SSR).

### Repetición de eventos

Hace unos meses el equipo de Angular anuncion un proyecto en curso a largo plazo que tenia como objetivo fusionar Angualar y el framework interno llamado Google Wiz. Angular y Wiz han estado sirviendo a dos segmentos diferentes de aplicaciones en el pasado: Wiz se usó principalmente para aplicaciones centradas en el consumidor, muy centrada en el rendimiento y Angular se centró en la productividad y la experiencia del desarrollador.

Como resultado de esta fusión Wiz integró profundamente Angular Signals en su modelo de renderizado. El equipo de Angular anunciuo que ahora Youtube usa Angular Signal, de la misma manera, Angular ahora ofrece cada vez más funciones centradas en el rendimiento, como la hidratación parcial, sobre las que compartiré más en un momento.

![Angular and Wize](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6cwftouuu8z9c0gjbii.png)


Ahora es posible registrar las interacciones del usuario durante la fase de hidratación y reproducirlas cuando la aplicación esté completamente cargada. Como sabrá, la fase de hidratación es la fase en la que el HTML renderizado por el servidor se transforma en una aplicación Angular completamente funcional, donde se agregan oyentes a los elementos existentes.

Pero durante esta fase, el usuario puede interactuar con la aplicación, y estas interacciones se pierden (si el proceso de hidratación no es lo suficientemente rápido).

Entonces, para algunas aplicaciones, puede resultar interesante registrar estas interacciones y reproducirlas cuando la aplicación esté completamente cargada.

#### Ejemplo:

Aunque la mayoría de los desarrolladores no trabajarán directamente con la reproducción de eventos, su utilidad es evidente. Imaginemos un sitio web de comercio electrónico con tiempos de carga lentos, como si tuviera una mala conexión. Un usuario intenta añadir varios productos al carrito mientras la página aún está cargando. Sin la reproducción de eventos, esas acciones se perderían.

Con Angular v18, la reproducción de eventos entra en juego: el sistema registra las acciones del usuario incluso antes de que la página sea interactiva. Una vez que la página carga completamente, los eventos se reproducen, y los productos se añaden al carrito como si no hubiera habido ningún problema de carga.


![Angular event replay example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8fe27qrvy8bxhqhjq6o.gif)


La función de repetición de eventos está disponible en la versión 18 en `developer preview`. Puedes habilitarlo usando `withEventReplay()`, por ejemplo:

```typescript
bootstrapApplication(App, {
  providers: [
    provideClientHydration(withEventReplay())
  ]
});
```

Para que la reproducción de eventos funcione, Angular inyecta un pequeño script al principio de tu página HTML. Este script tiene la misión de "reproducir" las acciones del usuario que ocurrieron mientras la página aún se estaba cargando.

¿Cómo lo hace? Primero, coloca un "oyente" en la raíz del documento que captura eventos específicos, como clics o doble clics, utilizando una técnica llamada delegación de eventos. Angular sabe qué eventos escuchar porque los recopiló durante el renderizado en el servidor.

El resultado final es que cuando la página está completamente cargada, este script inteligente reproduce las acciones del usuario, activando las funciones correspondientes como si nada hubiera pasado. Aunque no todas las aplicaciones se beneficiarán de esta característica, sin duda es una herramienta poderosa para aquellos casos donde la interacción temprana del usuario es crucial.

### Experiencia de depuración mejorada

Angular DevTools se ha actualizado para brindarte una mejor visión del proceso de hidratación. Ahora puedes ver el estado de hidratación de cada componente gracias a un nuevo ícono junto a su nombre. Y si quieres una vista general, activa el modo de superposición para ver al instante qué componentes ya están hidratados en la página. ¿Lo mejor? Si tu aplicación tiene errores de hidratación, Angular DevTools te los mostrará directamente en el explorador de componentes.


![Angular DevTools Hydration debugging](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9967dtx0kdr9dx16q1pv.png)

### Soporte para Hidratación en CDK y Material

En la versión 17, algunos componentes de Angular Material y CDK optaron por no hidratarse, lo que provocó su repetición. A partir de la versión 18, todos los componentes y primitivas son totalmente compatibles con la hidratación.

### Hidratación parcial

El equipo de Angular anuncio durante el Google I/O la hidratación parcial que es una técnica que te permite hidratar tu aplicación de forma incremental después del renderizado del lado del servidor. La hidratación incremental de tu aplicación permite cargar menos JavaScript por adelantado y mejora el rendimiento.

Imagina que puedes cargar tu aplicación web por partes, como si fuera un rompecabezas, empezando por las piezas más importantes. Eso es precisamente lo que permite la hidratación incremental: en lugar de cargar todo el código JavaScript de golpe, solo se carga lo esencial para mostrar la página rápidamente. El resto se va añadiendo sobre la marcha, mejorando significativamente el rendimiento.

La hidratación parcial lleva esta idea un paso más allá. Basándose en el concepto de "vistas diferibles", permite renderizar en el servidor el contenido principal de una sección específica de la página (marcada con @defer). Luego, en el navegador del usuario, Angular carga el código JavaScript asociado y "hidrata" esa sección solo cuando se cumplen ciertas condiciones, como por ejemplo, cuando el usuario interactúa con ella. De esta forma, se reduce la cantidad inicial de JavaScript y la página se vuelve más rápida e interactiva. Por ejemplo:


```typescript
@defer (render on server; on viewport) {
  <app-calendar/>
}
```

El bloque de arriba representará el componente de calendario en el servidor. Una vez que llegue al cliente, Angular descargará el JavaScript correspondiente e hidratará el calendario haciéndolo interactivo solo después de que ingrese a la ventana gráfica.

### Firebase App Hosting

Firebase presentó en Google I/O su nuevo servicio de App Hosting, diseñado para simplificar al máximo la creación y publicación de aplicaciones web dinámicas.

¿Qué lo hace tan especial? App Hosting se integra a la perfección con el ecosistema de Firebase, ofreciendo:

- **Soporte nativo para Angular:** Configuración y despliegue sin complicaciones.
- **Integración con GitHub:** Facilita el flujo de trabajo desde el desarrollo hasta la producción.
- **Conexión directa con otros servicios de Firebase:** Aprovecha al máximo Authentication, Cloud Firestore, Vertex AI y más, sin configuraciones complejas.

Con App Hosting, Firebase se convierte en una solución aún más completa para construir y escalar aplicaciones Angular de forma rápida y segura. Dale un vistazo a la [guía](https://firebase.google.com/docs/app-hosting/get-started)

## Nuevo paquete de construcción

Angular está simplificando su sistema de construcción de proyectos. Para ello, han creado un nuevo paquete llamado `@angular/build`, que se centra en las herramientas modernas como esbuild y Vite.

Antes, todas las herramientas de construcción, incluyendo Webpack, estaban juntas en @angular-devkit/build-angular. Al separar esbuild y Vite en su propio paquete, `@angular/build` se vuelve más ligero y eficiente.

Si actualizas tu proyecto a Angular v18, tendrás la opción de migrar a este nuevo sistema. La migración actualizará tu archivo angular.json para que use `@angular/build` en lugar de `@angular-devkit/build-angular` y ajustará las dependencias en tu package.json.

Pero no te preocupes, esta migración solo se realizará si no utilizas Webpack en tu proyecto. Si tus pruebas se basan en Karma, que a su vez usa Webpack, no se hará ningún cambio. En resumen, Angular te ofrece un camino más limpio y moderno para construir tus aplicaciones, sin afectar proyectos que aún dependen de Webpack.

## Redirecciones de ruta como funciones

Angular ahora te da más control sobre las redirecciones en tus aplicaciones. Antes, solo podías redirigir a una ruta fija, sin importar el contexto. ¡Pero eso ha cambiado!

Con Angular v18, la propiedad `redirectTo` de una ruta ahora acepta una función, llamada `RedirectFunction`, que te permite crear redirecciones dinámicas y mucho más inteligentes.

¿Cómo funciona? La `RedirectFunction` recibe información sobre la ruta actual, como parámetros o consultas, y en base a eso, decide a dónde redirigir al usuario. Además, puedes usar servicios dentro de esta función, lo que abre un abanico de posibilidades para crear reglas de redirección sofisticadas. Esta función también es similar a las guardias y se ejecuta en el inyector de entorno: esto significa que puede inyectar servicios si es necesario. La función puede devolver una cadena o un `UrlTree`. Por ejemplo:

```typescript
const routes: Routes = [
  { path: "my-component", component: MyComponent },
  {
    path: "user-page",
    redirectTo: ({ queryParams }) => {
      const errorHandler = inject(ErrorHandler);
      const userIdParam = queryParams['userId'];
      if (userIdParam !== undefined) {
        return `/user/${userIdParam}`;
      } else {
        errorHandler.handleError(new Error('Se intentó navegar a la página del usuario sin ID de usuario.'));
        return `/not-found`;
      }
    },
  },
  { path: "user/:userId", component: OtherComponent },
];
```

## HttpClientModule - Depreación

Angular sigue avanzando hacia un futuro donde los componentes independientes (Standolone components) son la norma, y esto significa que algunos módulos tradicionales están empezando a ser reemplazados. 

En Angular v18, los módulos `HttpClientModule` (junto con `HttpClientTestingModule`, `HttpClientXsrfModule`, y `HttpClientJsonpModule`) han sido marcados como obsoletos.  Esto no significa que ya no funcionen, pero sí que es hora de empezar a usar alternativas más modernas.

En su lugar, ahora puedes utilizar las funciones `provideHttpClient()` (con opciones para XSRF o JSONP) y `provideHttpClientTesting()`.  Estas funciones ofrecen una forma más directa y eficiente de configurar el cliente HTTP en tus aplicaciones.

Y como siempre, el equipo de Angular ha pensado en todo.  Al actualizar tu proyecto con `ng update @angular/core`,  se te preguntará si quieres migrar automáticamente tus módulos HTTP a la nueva sintaxis.

## Contenido alternativo para ng-content

`<ng-content>` es una característica poderosa en Angular, pero tiene una limitación: no puede tener contenido alternativo. ¡Este ya no es el caso en Angular v18!

Ahora podemos agregar algo de contenido dentro de la etiqueta `<ng-content>`, y este contenido se mostrará si no se proyecta ningún contenido en el componente.

Sólo necesitas poner el contenido alternativo dentro de la etiqueta `ng-content`:

```typescript
import { Component } from '@angular/core';

@Component({
  standalone: true,
  selector: 'my-component',
  template: `
    <ng-content select="header"> Default header </ng-content>

    <ng-content> Default main content </ng-content>

    <ng-content select="footer"> Default footer </ng-content>
  `,
})
export class MyComponent {}
```

Como puedes ver, esta nueva característica te permite definir fácilmente un contenido alternativo dedicado para cada elemento `ng-content` en tu componente.

Entonces, considerando el siguiente ejemplo:

```typescript
<my-component>
  <footer> New footer </footer>
</my-component>
```

El resultado será:

```typescript
Default header

Default main content

<footer> New footer </footer>
```

Nota: antes de Angular v18, esto provocaría un error.

## angular.dev - La nueva documentación oficial de Angular

El equipo de Angular ha anunciado que ahora angular.dev es el nuevo sitio web oficial de la documentación.


Angular v18 llega cargado de mejoras que hacen del desarrollo web una experiencia más fluida, eficiente y satisfactoria. Desde la optimización del rendimiento hasta la inclusión de nuevas funcionalidades, esta versión nos equipa con las herramientas necesarias para construir aplicaciones web de última generación. ¡No hay tiempo que perder, es hora de actualizar nuestros proyectos y sumergirnos en las posibilidades que nos ofrece Angular v18!

Muchas gracias por leerme, Espero que este recorrido por las novedades de Angular v18 te haya resultado útil. 

Si te ha gustado el artículo, ¡compártelo en tus redes sociales!

Sígueme en Twitter/X [ricardo_chl](https://twitter.com/ricardo_chl)