Angular 20: ¿Qué hay de nuevo?
Nuevos requisitos: TypeScript v5.8 y Node v20
Angular v20 no es nada ambiguo con sus nuevos requisitos. Ahora tienes que estar en TypeScript v5.8 (que, por cierto, ya era compatible desde v19.2), así que es hora de decir adiós a las versiones más antiguas de TypeScript).
Y para Node, dale la bienvenida a Node v20 como el nuevo mínimo. Angular v19 fue la gira de despedida para Node 18. ¡Es hora de actualizar esos entornos, colegas!
Guía de estilo 2025: ¡Una bocanada de aire fresco!
¡La Guía de Estilo de Angular ha recibido una renovación importante! Muchas recomendaciones se han reducido para centrarse en lo que realmente importa.
- Nombres de archivo recortados: Olvídate de
UserComponent
enuser.component.ts
. Ahora es simplementeUser
enuser.ts
. Lo mismo para directivas, pipes, etc. La CLI ya está aplicando esto para cosas nuevas. ¿Cuánto tardará tu memoria muscular en adaptarse? - Estructura de carpetas más ligera: La carpeta
app
de nivel superior podría desaparecer pronto (aunque la CLI aún no ha llegado a ese punto). - Visibilidad de propiedades:
protected
es ahora la forma para las propiedades que solo se usan en tu plantilla, yreadonly
para todas esas propiedades inicializadas por Angular (input()
,output()
, etc.). ¡Tiene sentido! - Bindings de Clases y Estilos: ¡Es oficial!
[class.something]
y[style.something]
son los campeones recomendados sobrengClass
yngStyle
.
Esto es un cambio grande. Los nuevos proyectos lo adoptarán por defecto. ¿Los existentes? Bueno, o migras o te quedas con las viejas costumbres (la CLI ayuda con una configuración para eso, ¡uf!).
APIs de Signals: ¡Mayormente Luz Verde y Estables!
¡Las Signals son el futuro, y las APIs se están consolidando! La mayoría ahora son estables:
effect()
toSignal()
toObservable()
afterRenderEffect()
afterNextRender()
linkedSignal()
PendingTasks
¡Ojo con esto! afterRender()
ha sido renombrado a afterEveryRender()
y es estable. Crucialmente, el nombre antiguo desapareció sin migración automática. ¡Uf, eso podría doler!
Además, TestBed.flushEffects()
(esa API en developer preview un poco escurridiza) está obsoleta. Usa TestBed.tick()
ahora, que ejecuta todo el proceso de sincronización, mucho más cercano al comportamiento real de la aplicación. effect()
perdió su opción forceRoot
(¿alguien la usaba mucho?), y toSignal()
eliminó rejectErrors
(¡bien pensado, mejores prácticas al poder!). pendingUntilEvent()
todavía se está calentando en developer preview.
Zoneless: ¡Fuera de Experimental, entra en Developer Preview!
Si las signals son el futuro de la reactividad, ¡Zoneless es el futuro de la detección de cambios! Ya no es "experimental", ahora está oficialmente en developer preview.
provideExperimentalZonelessChangeDetection
ahora es simplementeprovideZonelessChangeDetection
.- El flag de la CLI
--experimental-zoneless
ahora es simplemente--zoneless
.
La CLI incluso te preguntará si quieres habilitarlo para nuevos proyectos. ¿Listos para el #NoZone?
Que se va, que se queda y que puede romper nuestro código:
Como en toda versión mayor, algunas cosas se van a la basura:
- Las directivas
ngIf
,ngFor
,ngSwitch
están oficialmente obsoletas. El control de flujo incorporado (@if
,@for
,@switch
) es el rey ahora. Empieza a migrar; ¡probablemente desaparecerán en v22!ng update
te echará una mano. fixture.autoDetectChanges(boolean)
: El parámetro booleano ha desaparecido. Simplemente usafixture.autoDetectChanges()
. ¿Usarfixture.autoDetectChanges(false)
en un test zoneless? Eso ahora lanzará un error.TestBed.get()
: ¡Finalmente, finalmente DESAPARECIÓ! Estuvo obsoleto desde allá por Angular v9.TestBed.inject()
es tu amigo (desde hace mucho). Una migración automática se encargará de esto.- Enum
InjectFlags
: Eliminado. Los objetos de opciones para las APIs de DI (comoinject()
) han sido la norma desde v14.1. - Token
DOCUMENT
: Se movió de@angular/common
a@angular/core
. Una migración actualizará tus imports. @angular/platform-browser-dynamic
: Obsoleto en favor de@angular/platform-browser
. Tendrás que actualizar manualmente los imports para este por ahora.@angular/platform-server/testing
: También obsoleto, sin reemplazo. Las pruebas E2E son ahora la forma recomendada para verificar aplicaciones SSR.- Integración con HammerJS: Obsoleta. HammerJS no ha visto una actualización en 8 años. Es hora de decir adiós a esas entidades en el framework.
- Atributos
ng-reflect-*
: Desaparecen por defecto en modo desarrollo. Eran para antiguas devtools. Si dependías de ellos (¡probablemente no deberías!), puedes rehabilitarlos conprovideNgReflectAttributes()
. Pero quizás... ¿mejor no?
Plantillas: ¡Nuevos trucos bajo la manga!
Tus plantillas se han vuelto un poco más expresivas:
- Exponenciación:
{{ 2 ** 3 }}
ahora es posible. ¡Por lo que podemos hacer cálculos de manera más fácil!. - Tagged Template Literals: Sí,
{{ translate\
app.title` }}` está aquí (técnicamente desde v19.2, pero ahora está asentado). - Operador
void
: Úsalo como<button (click)="void selectUser()">
para ignorar explícitamente el retorno de una función, especialmente útil para listeners de eventos dondereturn false
podría prevenir el comportamiento por defecto. - Operador
in
: Verifica propiedades como@if ('invoicing' in permissions)
. ¡Súper útil!
Diagnósticos extendidos: ¡El compilador te mantiene informado!
Más verificaciones incorporadas para atrapar errores comunes:
missingStructuralDirective
: ¿Usando algo como*ngTemplateOutlet
pero olvidaste importarNgTemplateOutlet
? El compilador ahora te lo hará saber (constrictTemplates
).uninvokedTrackFunction
: ¿Escribiste@for (user of users; track getUserId)
en lugar detrack getUserId(user)
? Recibirás un codazo amistoso.unparenthesizedNullishCoalescing
: Mezclar??
con&&
o||
(ej.a ?? b && c
) ahora requiere paréntesis para mayor claridad, como{{ a ?? (b && c) }}
. TypeScript hace esto, ¡así que es genial verlo también en las plantillas!
Puedes suprimirlos en tsconfig.json
si de verdad quieres, pero... ¿Por qué querrías?
Chequeo de tipos en host: ¡Ya no más misterios!
Hay una nueva opción de compilador typeCheckHostBindings
(ya en nuevos proyectos CLI). Si usas metadatos de host en decoradores (o @HostBinding
/@HostListener
), el compilador ahora verifica:
- Si el objetivo del binding/listener (ej.
value
en[value]="value()"
) es realmente válido para el elemento host. - Si la propiedad en tu clase de componente/directiva (ej.
value()
) realmente existe.
¡Esto es brutal para pillar typos y asegurar que tus bindings de host sean legítimos!
Manejo de errores: ¡Menos errores que se escapan!
provideBrowserGlobalErrorListeners
: Un nuevo provider (agregado por defecto en nuevos proyectos CLI) para registrar listeners de errores globales en el navegador. Atrapa errores que Angular podría pasar por alto.- Los errores en los listeners de eventos ahora se reportan al manejador de errores interno de Angular. Esto significa que podrías ver errores en tests que antes eran silenciosos. Es hora de arreglarlos (o usar
rethrowApplicationErrors: false
enconfigureTestingModule
como último recurso).
Componentes creados dinámicamente: ¡Sube de nivel!
¡createComponent()
(y ViewContainerRef.createComponent
) se volvieron mucho más geniales en v20! Ahora puedes pasar opciones para:
- Especificar
directives
a aplicar al componente dinámico. - Proveer valores de input usando la nueva función
inputBinding()
. - Declarar two-way bindings con
twoWayBinding()
. - Escuchar outputs con
outputBinding()
.
Esto es un gran avance respecto a llamar setInput
después de la primera detección de cambios. ¡Más poder y control! ¿Llegará esto también a TestBed.createComponent()
?
Formularios: Aún esperando las señales, pero...
No hay formularios basados en signals todavía (¡todos estamos al borde del asiento esperando eso!). Pero v20 trae un par de ajustes pequeños pero bienvenidos:
userForm.resetForm(undefined, { emitEvent: false })
: Resetea formularios sin disparar eventos.markAllAsDirty()
: Finalmente, un método enAbstractControl
para marcar un control y todos sus descendientes comodirty
. ¡AmarkAllAsTouched()
le faltaba un hermano!
Router: ¡Navegación más fluida a la vista!
Algunas mejoras interesantes para el router:
- Opciones de Scroll: Pasa opciones nativas de scroll a
ViewportScroller.scrollToAnchor()
/scrollToPosition()
. Por ejemplo,behavior: 'smooth'
para ese efecto de scroll elegante. - Resolvers con más contexto: ¡Los resolvers de rutas hijas ahora pueden acceder a datos resueltos de su ruta padre!
route.data.user
del padre estará disponible. ¡Less gimnasia para obtener datos! - Redirecciones asíncronas: La opción
redirectTo
en las configuraciones de ruta ahora puede aceptar una función que devuelva una Promesa o un Observable para redirecciones asíncronas. (Técnicamente un breaking change debido a la evolución del tipo de retorno). - Soporte para Custom Elements: ¿Escribiendo Web Components? Ahora puedes usar un custom element como host de un
RouterLink
.
Http: APIs de resource
Evolucionando y keepalive
!
- Cambios en API de
resource
: El parámetroquery
deresource()
ahora esparams
. PararxResource()
,loader
ahora esstream
. El métodoreload
se movió aWritableResource
(solo los recursos mutables pueden recargarse). - Actualizaciones en
httpResource
: La opciónmap
ahora esparse
. Puedes especificar el contexto HTTP en las opciones. Y, la petición debe ser ahora reactiva (ej.httpResource<User[]>(() => '/users')
en lugar de solo la URL como string). - Soporte
keepalive
:HttpClient
ahora soporta la opciónkeepalive
cuando se usa la API Fetch (habilitada conwithFetch()
). Las peticiones no se abortarán si la página se descarga. Útil para cosas como analíticas.
Profiling: ¡Mira dónde es afectado el rendimiento de tu app!
Hay una nueva función enableProfiling()
en @angular/core
. Llama a esto, y Angular usará la API de Performance del navegador para etiquetar operaciones del framework (detección de cambios, plantillas, outputs, defer, etc.). Luego, abre las Devtools de Chrome, graba un perfil de rendimiento y mira la pista personalizada "Angular". ¡Por fin, una vista clara del rendimiento interno!
Devtools: ¡Mejores Perspectivas!
Las Devtools de Angular se están volviendo más inteligentes:
- Los componentes
OnPush
ahora se marcan como tales en el árbol de componentes. - Los bloques diferidos (
defer
) también se muestran. - El soporte para Signals está mejorando – ¡pronto deberíamos ver el árbol de signals! Echar un vistazo bajo el capó ahora es más fácil.
SSR: ¡APIs estables y configuración optimizada!
Buenas noticias para el Server-Side Rendering:
- ¡Las APIs
withI18nSupport()
ywithIncrementalHydration()
ahora son estables! provideServerRendering()
(ahora en@angular/ssr
en lugar de@angular/platform-server
) se combina conprovideServerRoutesConfig()
en una única funciónprovideServerRendering(withRoutes(serverRoutes))
. Una migración se encargará de esto.- Las nuevas apps CLI con
--ssr
obtienen Express v5 y soporte de enrutamiento en servidor por defecto (la opción--server-routing
ha desaparecido).
Angular CLI: ¡Esto es ENORME!
La CLI vio una tonelada de cambios. Prepárense:
- Nomenclatura actualizada para Guía de estilo 2025: Como se mencionó,
user.ts
(con claseUser
) en lugar deuser.component.ts
(claseUserComponent
). Igual para directivas, servicios. Pipes, resolvers, etc. usan guiones en los nombres de archivo (from-now-pipe.ts
). Una migración deangular.json
configura tus proyectos existentes para usar la convención antigua si quieres una transición suave - Configuración de TypeScript: La opción
module
ahora espreserve
(refleja mejor los bundlers modernos).tsconfig.json
usa un estilo de "solución", referenciandotsconfig.app.json
ytsconfig.spec.json
. angular.json
simplificado: Los nuevos proyectos usan@angular/build
directamente, deshaciéndose de@angular-devkit/build-angular
y sus dependencias transitivas de Webpack. ¡Eso es casi 200 Mb menos ennode_modules
! Algunas opciones comooutputPath
también se eliminan ya que tienen valores por defecto sensatos. ¡Más ligero y potente!- Configuración de Browserslist: Ahora apunta a la base "ampliamente disponible" (navegadores lanzados hace < 30 meses del conjunto principal de Baseline). Soporte de navegadores más consistente y realista.
- Importadores de paquetes Sass: Ahora puedes usar importadores
pkg:
, como@use 'pkg:@angular/material' as mat;
. - Testing Ahead of Time (AoT) y Cobertura de Código para Plantillas: Añade
"aot": true
a tus opciones de test enangular.json
. ¡Ejecuta tests en el mismo modo que producción! Además, obtienes cobertura de código para las plantillas. - Testing con Vitest (¡Experimental!): ¡Notición! La CLI ahora soporta ejecutar tests con Vitest. Hay un nuevo builder
@angular/build:unit-test
. Karma y Jasmine podrían tener un nuevo retador.- Configúralo con
"runner": "vitest"
. Necesitarás un"buildTarget"
. - Por defecto, usa Node con
jsdom
(instálalo). - Actualiza los
types
detsconfig.spec.json
a["vitest/globals"]
. - Modo Navegador: ¡Sí!
"browsers": ["chromium"]
(o firefox, webkit). Usa Playwright o WebdriverIO (instala uno). - El modo watch es más rápido (solo ejecuta tests afectados). Las ejecuciones completas podrían ser un poco más lentas que Karma.
--no-watch
a menudo es implícito en CI.- Nuevo flag
--debug
(Vitest + jsdom/Playwright). - Limitaciones: Aún no hay archivo de configuración de Vitest personalizado. Pero la opción
providersFile
te permite configurar cosas como testing zoneless. Reporter y exclusiones de cobertura están enangular.json
.
- Configúralo con
- Carpetas de Workspace automáticas en Chrome: ¡El servidor de desarrollo Vite ahora ayuda a las Devtools de Chrome a mapear los archivos de tu proyecto, permitiendo edición directa desde Devtools que se guarda en tu disco! Habilítalo en los flags de Chrome.
- Sourcemaps sin fuentes: Genera sourcemaps sin incrustar el código fuente original (
"sourcesContent": false
). Genial para reportar errores en producción sin exponer todo tu código.
¡Angular y su futuro junto a la IA!
Angular se posiciona para facilitar el desarrollo de aplicaciones con capacidades de IA generativa:
Archivo: llms.txt
Se mantiene un archivo llms.txt
en el repositorio de Angular. Este archivo ayuda a los grandes modelos de lenguaje (LLMs) a descubrir la documentación y los ejemplos de código más recientes y correctos de Angular, para que generen código más moderno y preciso, evitando problemas con APIs o sintaxis obsoletas.
Guías y recursos: Se están proporcionando guías y recursos, angular.dev/ai incluyendo ejemplos y live streams que muestran cómo integrar Angular con herramientas como Genkit y Vertex AI de Google Cloud.
Qué podríamos llegar a ver en Angular v21
- Componentes sin selector: Imagina esto:
¡Componentes usados por su nombre de clase directamente en las plantillas! Las directivas podrían usar un prefijoimport { User } from './user/user'; // El import de TS aún es necesario @Component({ template: '<User [name]="name()" (selected)="selectUser()" />', // ¡NO se necesita el array 'imports' de Angular para User! }) export class App { /* ... */ }
@
(ej.<User @CdkDrag />
). Los pipes también por nombre de clase ({{ date | FromNowPipe }}
). La sintaxis está LEJOS de ser final, pero el trabajo en el compilador ha comenzado. ¿Alucinante, no? ¡Esto podría ser revolucionario! Debería llegar un RFC. - Formularios con Signals (¿La Tercera Vía?): ¡Prepárense para una posible nueva forma de hacer formularios, separada de template-driven y reactive!
Una nueva función// PROTOTIPO MUY TEMPRANO - ¡NO USAR AÚN! @Component({ selector: "user-form", imports: [FieldDirective], // Nueva directiva template: ` <form> <label>Username: <input [field]="userForm.username" /></label> <label>Name: <input [field]="userForm.name" /></label> </form> `, }) class UserFormComponent { userModel = signal<UserModel>({ /* ... */ }); protected readonly userForm: Field<User> = form<User>( // nueva función form() userModel, // datos a editar (userPath: FieldPath<User>) => { // esquema para comportamiento dinámico y validación disabled(userPath.username, () => true, "No se puede cambiar el nombre de usuario"); required(userPath.name); error(userPath.age, ({ value }) => value() < 18, "Debe ser mayor de 18"); } ); }
form()
y claseField
para manejar el estado del formulario con signals. ¡Echa un vistazo al design doc si tienes curiosidad, o espera al RFC!