Angular está pasando por un gran cambio de imagen. El framework web hecho por Google y que utiliza TypeScript se está preparando para la versión 16, que tendrá muchas cosas nuevas y emocionantes. Por ejemplo, facilitará el uso del renderizado del lado del servidor, lo que significa páginas web más rápidas y fluidas. También tendrá un mejor rendimiento en tiempo de ejecución, lo que significa menos errores y fallos.

La versión 16 estará disponible a partir de hoy. Tiene más cambios que cualquier versión principal anterior (excepto por el salto de Angular a Angular 2).

Angular es cada vez más moderno y potente, y la versión 16 es sólo el comienzo. Estas son algunas de las cosas interesantes que puedes esperar en esta versión.

TENEMOS WORKSHOP 🎉 APUNTATE!

SIGNALS

Una nueva forma de gestionar los cambios de estado


Una de las características más interesantes de Angular 16 es la introducción de signals, una nueva forma de gestionar los cambios de estado en las aplicaciones Angular. Los signals están inspiradas en Solid.js, una biblioteca de interfaz de usuario reactiva que utiliza un patrón push/pull para crear gráficos de valores reactivos.

Los signals son funciones que devuelven un valor (get) y pueden actualizarse llamándolas con un nuevo valor (set).

También pueden depender de otras señales, creando un gráfico de valores reactivo que se actualiza automáticamente cuando cambia cualquier dependencia. Los signals pueden utilizarse con observables RxJS, que siguen siendo compatibles con Angular 16, para crear flujos de datos potentes y declarativos.

Ofrecen varias ventajas sobre el mecanismo tradicional de detección de cambios en Angular, que se basa en Zone.js para parchear las API del navegador y activar la detección de cambios de forma global.

Permiten ejecutar la detección de cambios sólo en los componentes afectados, sin recorrer todo el árbol de componentes ni utilizar Zone.js. Esto mejora el rendimiento en tiempo de ejecución y reduce la complejidad.

Si tu quieres aprender angular recuerda ir al video

También hacen que tu código sea más expresivo y fácil de razonar, ya que puedes ver claramente las dependencias y actualizaciones de tus valores. Las señales también permiten una reactividad de grano fino, lo que significa que puedes controlar cuándo y cómo cambian tus valores.

He aquí un posible ejemplo de uso de señales en Angular 16:

// Import the createSignal function
import { createSignal } from '@angular/core';

// Create a component that uses signals
@Component({
  selector: 'app-counter',
  template: `
    <h1>{{ count }}</h1>
    <button (click)="increment()">+</button>
    <button (click)="decrement()">-</button>
  `
})
export class CounterComponent {
  // Create a signal instance for the count value
  count = createSignal(0);

  // Increment the count value by calling the next method on the signal
  increment() {
    this.count.next(this.count.value + 1);
  }

  // Decrement the count value by calling the next method on the signal
  decrement() {
    this.count.next(this.count.value - 1);
  }
}

Renderizado del lado del servidor: Más rápido y suave


Otra mejora importante en Angular 16 es la compatibilidad con la hidratación no destructiva, que hace que el renderizado del lado del servidor (SSR) sea más rápido y fluido. SSR es una técnica que renderiza tu aplicación en el servidor y envía el contenido HTML al navegador, donde se convierte en una página web completamente interactiva y funcional adjuntando comportamiento JavaScript y escuchadores de eventos.

SSR reduce el tiempo hasta la interactividad (TTI) y mejora el SEO y la accesibilidad. SSR ha estado disponible en frameworks como React o Next.js durante un tiempo, pero no era fácil de implementar en Angular.

En Angular 16, SSR será soportado fuera de la caja, haciendo que las aplicaciones SSR sean más rápidas y fluidas.

La hidratación no destructiva significa que el navegador no sobrescribe ni elimina ningún contenido o atributo HTML existente al hidratar la aplicación. Esto preserva cualquier optimización o personalización del lado del servidor que pueda haber aplicado a su contenido HTML.

La hidratación no destructiva también evita posibles conflictos o errores que puedan surgir de un contenido HTML no coincidente entre el servidor y el cliente.

Experiencia del desarrollador:

Mejor y más fácil


Angular 16 también introduce algunas nuevas características y mejoras que facilitan la experiencia del desarrollador y la calidad del código de las aplicaciones Angular. Algunas de estas características son:

  • Entradas obligatorias del componente: Esta característica permite marcar algunas entradas de un componente como obligatorias, lo que significa que el componente padre debe proporcionarlas o, de lo contrario, se lanzará un error. Esto ayuda a detectar errores y erratas en tiempo de compilación y garantiza que los componentes reciban todos los datos necesarios para funcionar correctamente.

Las entradas obligatorias también hacen que los componentes sean más autodocumentados y fáciles de usar.

@Component({})
class HomeComponent {
  @Input({required: true}) someRequiredInput;
}
  • API de depuración de inyección de dependencias: Esta característica te permite inspeccionar y depurar el sistema de inyección de dependencias en aplicaciones Angular. Puedes utilizar estas API para obtener información sobre los proveedores, tokens, inyectores, ámbitos e instancias de tus dependencias. También puedes usar estas APIs para simular diferentes escenarios o casos de prueba para tus dependencias.
    He aquí un posible ejemplo de uso de las API de depuración de inyección de dependencias en Angular 16:
// Import the DebugInjector interface
import { DebugInjector } from '@angular/core';

// Inject the DebugInjector service in the constructor
constructor(private debugInjector: DebugInjector) {}
// Use the DebugInjector service to get information about the dependencies
ngOnInit() {
  // Get the provider for a token
  const provider = this.debugInjector.getProvider(MyService);
  // Get the injector for a token
  const injector = this.debugInjector.getInjector(MyService);
  // Get the scope for a token
  const scope = this.debugInjector.getScope(MyService);
  // Get the instance for a token
  const instance = this.debugInjector.getInstance(MyService);
  // Simulate a different scenario for a token
  this.debugInjector.simulate(MyService, {
    // Provide a different value for the token
    value: new MyService('test'),
    // Override the scope for the token
    scope: 'root',
    // Override the injector for the token
    injector: this.debugInjector.createInjector([MyService])
  });
  // Reset the simulation for a token
  this.debugInjector.reset(MyService);
}

En este ejemplo, creamos un componente simple que utiliza las APIs de depuración de inyección de dependencias para obtener y manipular información sobre sus dependencias. Importamos la interfaz DebugInjector de @angular/core y la inyectamos como un servicio en el constructor. A continuación, utilizamos varios métodos en el servicio DebugInjector para acceder y modificar el proveedor, el inyector, el ámbito y la instancia de un token de dependencia.

También podemos utilizar los métodos simulate y reset para crear diferentes escenarios o casos de prueba para nuestras dependencias. De esta forma, podemos depurar y solucionar problemas de nuestro sistema de inyección de dependencias de forma más fácil y eficaz.

  • Documentación y esquemas mejorados para componentes independientes: Esta función mejora la documentación y los esquemas para crear componentes independientes en aplicaciones Angular. Los componentes independientes son componentes que no pertenecen a ningún módulo y pueden utilizarse en cualquier parte de la aplicación. Los componentes independientes son útiles para crear elementos de interfaz de usuario o bibliotecas reutilizables.
  • Exploración de opciones para mejorar los paquetes JavaScript creados por la CLI de Angular: Esta función explora diferentes opciones para optimizar los paquetes JavaScript creados por la CLI de Angular, como el uso de módulos ES, tree-shaking, code-splitting o carga diferencial. El objetivo de estas opciones es reducir el tamaño del paquete y mejorar el rendimiento de carga de las aplicaciones de Angular.

Otras características y mejoras


Angular 16 también añade algunas otras características y mejoras que mejoran la funcionalidad y usabilidad del framework. Algunas de estas características son:

  • Soporte para Tailwind CSS: Tailwind CSS es un popular framework CSS utility-first que te permite dar estilo a tus componentes con clases en lugar de escribir CSS personalizado. Tailwind CSS ofrece muchas ventajas, como un desarrollo más rápido, un diseño coherente, un diseño adaptable, personalización y extensibilidad. Angular 16 es compatible con Tailwind CSS desde el primer momento, lo que facilita su uso en tus proyectos Angular.
  • Compatibilidad con el aislamiento de CSS: El aislamiento de CSS es una función que te permite delimitar los estilos de tus componentes para evitar que se filtren o entren en conflicto con otros estilos. El aislamiento de CSS puede lograrse utilizando shadow DOM o encapsulación emulada. Angular 16 admite ambas opciones y te permite elegir la que mejor se adapte a tus necesidades.
    Aquí tienes un posible ejemplo de uso del aislamiento CSS en Angular 16:
// Import the ViewEncapsulation enum
import { Component, ViewEncapsulation } from '@angular/core';

// Create a component that uses shadow DOM for CSS isolation
@Component({
  selector: 'app-shadow',
  template: `
    <h1>Shadow DOM</h1>
    <p>This component uses shadow DOM for CSS isolation.</p>
  `,
  styles: [`
    h1 {
      color: blue;
    }
  `],
  // Set the encapsulation property to ViewEncapsulation.ShadowDom
  encapsulation: ViewEncapsulation.ShadowDom
})
export class ShadowComponent {}
// Create a component that uses emulated encapsulation for CSS isolation
@Component({
  selector: 'app-emulated',
  template: `
    <h1>Emulated Encapsulation</h1>
    <p>This component uses emulated encapsulation for CSS isolation.</p>
  `,
  styles: [`
    h1 {
      color: red;
    }
  `],
  // Set the encapsulation property to ViewEncapsulation.Emulated (default value)
  encapsulation: ViewEncapsulation.Emulated
})
export class EmulatedComponent {}

El primer componente utiliza shadow DOM, que es una función nativa del navegador que crea un árbol DOM y un ámbito de estilos independientes para el componente. El segundo componente utiliza encapsulación emulada, que es una característica de Angular que añade atributos únicos a los elementos y estilos del componente para simular el comportamiento de shadow DOM.

  • Soporte para Trusted Types nativos: Trusted Types es una característica del navegador que ayuda a prevenir ataques de cross-site scripting (XSS) mediante la aplicación de reglas estrictas sobre cómo se utilizan las cadenas en contextos sensibles. Los tipos de confianza pueden evitar que se ejecute código malicioso al desinfectar o rechazar cadenas no seguras. Angular 16 soporta Trusted Types nativos y los aplica automáticamente a tus plantillas y expresiones.
    He aquí un posible ejemplo de uso de Trusted Types en Angular 16:
// Import the createTrustedHTML function
import { createTrustedHTML } from '@angular/core';

// Create a component that uses Trusted Types
@Component({
  selector: 'app-hello',
  template: `
    <div [innerHTML]="message"></div>
  `
})
export class HelloComponent {
  // Create a trusted HTML value using the createTrustedHTML function
  message = createTrustedHTML('<h1>Hello, world!</h1>');
  // Alternatively, use the safevalues library to create trusted values
  // import { createHtml } from 'safevalues/implementation/html_impl';
  // message = createHtml('<h1>Hello, world!</h1>');
}
  • Soporte para importaciones dinámicas de datos de enrutador: Esta característica es una nueva forma de obtener información del enrutador en tu componente sin inyectar el servicio ActivatedRoute. Con esta función, puede vincular algunos datos del enrutador a las entradas de su componente, como los parámetros de ruta, los datos, los parámetros de consulta, el fragmento y la url. Esto puede hacer que tu código sea más simple y limpio.
    Por ejemplo, en lugar de escribir esto:
// Import the ActivatedRoute service
import { ActivatedRoute } from '@angular/router';

// Inject the ActivatedRoute service in the constructor
constructor(private route: ActivatedRoute) {}

// Use the ActivatedRoute service to get the router data
ngOnInit() {
  // Get the route params
  this.route.params.subscribe(params => {
    // Do something with params
  });

  // Get the route data
  this.route.data.subscribe(data => {
    // Do something with data
  });

  // Get the query params
  this.route.queryParams.subscribe(queryParams => {
    // Do something with queryParams
  });

  // Get the fragment
  this.route.fragment.subscribe(fragment => {
    // Do something with fragment
  });

  // Get the url
  this.route.url.subscribe(url => {
    // Do something with url
  });
}

Puedes escribir esto:

// Define the component inputs for the router data
@Input() params: Params;
@Input() data: Data;
@Input() queryParams: Params;
@Input() fragment: string;
@Input() url: UrlSegment[];

// Use the component inputs to get the router data
ngOnInit() {
  // Do something with params
  // Do something with data
  // Do something with queryParams
  // Do something with fragment
  // Do something with url
}

Para utilizar esta función, debe activarla en el módulo RouterModule estableciendo la opción bindToComponentInputs en true. Por ejemplo:

// Import the RouterModule and Routes
import { RouterModule, Routes } from '@angular/router';

// Define the routes for the application
const routes: Routes = [
  // Use dynamic imports to load modules lazily
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
  },
  {
    path: 'about',
    loadChildren: () => import('./about/about.module').then(m => m.AboutModule)
  },
  {
    path: 'contact',
    loadChildren: () => import('./contact/contact.module').then(m => m.ContactModule)
  }
];

// Import the modules for the application
@NgModule({
  imports: [
    // Use the RouterModule.forRoot method to register the routes and enable bindToComponentInputs option
    RouterModule.forRoot(routes, { bindToComponentInputs: true })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule {}
  • Un nuevo componente selector de rango de fechas en Angular Material: Angular Material es una biblioteca de componentes de interfaz de usuario que implementa las directrices de Material Design. Angular Material proporciona muchos componentes que se pueden utilizar para crear interfaces de usuario bonitas y funcionales. Angular 16 añade un nuevo componente selector de intervalo de fechas a Angular Material, que permite seleccionar una fecha de inicio y de finalización de un calendario.

Conclusión


Angular 16 es una gran actualización que trae muchas mejoras y nuevas características al framework. Angular 16 está listo para revolucionar la gestión de estados con signals. Con la mejora de la experiencia del desarrollador y la hidratación natural de las aplicaciones de renderizado del lado del servidor, los desarrolladores tendrán una mayor flexibilidad para crear interfaces de usuario atractivas.

Otras características citadas por Google para Angular 16 incluyen la revisión del modelo de reactividad de Angular y hacer que Zone.js sea opcional para mejorar el rendimiento en tiempo de ejecución, la introducción de API de depuración de inyección de dependencias, la mejora de la documentación y los esquemas de los componentes independientes, la exploración de opciones para mejorar los paquetes de JavaScript creados por Angular CLI y la refactorización de documentos.