← Volver al blog Post

Cómo actualizar a Angular 6 y no morir en el intento

Publicado originalmente en Medium.

Mantener una aplicación actualizada es una de las tareas más importantes para cuidar su seguridad, su rendimiento y la salud general del proyecto. El problema es que, cuando llega una nueva major version, esa actualización puede convertirse fácilmente en un pequeño calvario.

Ese fue precisamente el caso de Angular 6. La nueva versión introdujo varios cambios de arquitectura que no afectan tanto cuando arrancas un proyecto desde cero, pero sí pueden complicar bastante la migración de una aplicación ya existente.

Por suerte, el propio equipo de Angular publicó una guía para acompañar el proceso:

Angular Update Guide

Esa guía ayuda mucho con el ecosistema oficial, pero una aplicación real no suele depender solo de Angular. También usamos librerías como NgRx para el estado o AngularFire2 para consumir Firebase, y ahí es donde empiezan a aparecer matices fuera del camino feliz.

La mayor fuente de fricción en esta migración está en RxJS 6. El salto de versión cambia la forma de importar y encadenar operadores, y eso puede afectar tanto a librerías externas como al propio código de la aplicación.

Cambiar la versión

El primer paso es seguir la estrategia oficial. La guía de actualización de Angular permite indicar desde qué versión partes, a cuál quieres llegar y si usas herramientas adicionales. Con esa información genera una lista de tareas concreta para tu caso.

Lo interesante es que no se limita a soltar comandos. También explica por qué existe cada paso y enlaza a la documentación oficial correspondiente, algo especialmente útil cuando aparecen errores de dependencias o advertencias nuevas del compilador.

Al terminar la migración, la propia guía suele sugerir eliminar rxjs-compat cuando todo el proyecto ya esté adaptado.

Qué es rxjs-compat

rxjs-compat existe para mantener compatibilidad con código escrito siguiendo los patrones antiguos de RxJS. Si lo dejas instalado, una parte importante del código seguirá funcionando sin demasiados cambios.

Eso tiene una ventaja inmediata: puedes actualizar primero la base del proyecto y posponer una migración más profunda del código reactivo.

También tiene un coste: sigues arrastrando una capa de compatibilidad que no debería quedarse indefinidamente en producción. Si el objetivo es terminar con una base limpia, tarde o temprano conviene retirarla y adaptar el código de verdad.

NgRx

En el caso de NgRx, la actualización suele ser bastante directa porque sus paquetes entran dentro del flujo habitual de ng update.

Por ejemplo:

ng update @ngrx/core

Según la versión concreta del proyecto, puede que también toque revisar @ngrx/store, @ngrx/effects o @ngrx/entity, pero la complejidad aquí suele ser menor que con el código reactivo escrito a mano.

Firebase y AngularFire2

Si la aplicación usa Firebase, probablemente también use firebase y angularfire2. Ambas dependencias estaban condicionadas por el cambio de versión de RxJS, así que conviene actualizarlas de forma coordinada.

Una opción práctica es subir ambas a las versiones compatibles más recientes:

npm install firebase@next angularfire2@next

En aquel momento esta era la forma más rápida de alinearlas con Angular 6 y RxJS 6 sin quedarse atrapado entre rangos de dependencias incompatibles.

El verdadero trabajo: el código

Hasta aquí todo parece razonable. El problema real llega cuando toca adaptar el código de la aplicación.

RxJS 6 cambia los imports. Donde antes era común escribir algo así:

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

Ahora los imports pasan a centralizarse en rxjs y rxjs/operators:

import { Observable, of } from 'rxjs';
import { catchError, map, mergeMap, switchMap, take } from 'rxjs/operators';

No es solo una cuestión estética. También cambia la forma de encadenar operadores.

Antes era habitual escribir:

this.myObservable().map((data) => {
  // ...
});

Con RxJS 6, los operadores pasan por pipe:

this.myObservable().pipe(
  map((data) => {
    // ...
  })
);

Además, algunos métodos estáticos y operadores cambian de forma:

return Observable.of();

se convierte en:

return of();

Y catch() pasa a ser catchError():

catchError(() => of(fallbackValue))

Un ejemplo real

Un efecto de NgRx escrito con la sintaxis anterior podía tener este aspecto:

getUser: Observable<Action> = this.actions.ofType(userActions.GET_USER)
  .map((action: userActions.GetUser) => action.payload)
  .switchMap(() => this.auth.user$)
  .map((authData) => {
    if (authData) {
      return new userActions.Authenticated({ ...authData });
    }

    return new userActions.NotAuthenticated();
  })
  .catch(() => Observable.of(new userActions.AuthError()));

Tras la migración, la misma idea pasa a escribirse así:

getUser: Observable<Action> = this.actions.ofType(userActions.GET_USER).pipe(
  map((action: userActions.GetUser) => action.payload),
  switchMap(() => this.auth.user$),
  map((authData) => {
    if (authData) {
      return new userActions.Authenticated({ ...authData });
    }

    return new userActions.NotAuthenticated();
  }),
  catchError(() => of(new userActions.AuthError()))
);

La diferencia no es pequeña cuando el proyecto lleva tiempo creciendo y existen decenas de flujos reactivos repartidos entre servicios, efectos y componentes.

Conclusión

Angular pone bastantes facilidades para actualizar la versión del framework, pero no todo puede automatizarse. La parte delicada de la migración está en entender el cambio mental que introduce RxJS 6 y aplicarlo con cuidado al código ya existente.

Una vez identificas ese patrón, el proceso deja de ser misterioso y pasa a ser principalmente trabajo mecánico: actualizar imports, mover operadores a pipe, reemplazar helpers antiguos y revisar dependencias del ecosistema.

Si estás en pleno salto a Angular 6, la recomendación práctica es clara:

  • usa la guía oficial para la base de Angular,
  • actualiza dependencias como NgRx y AngularFire2 de forma coordinada,
  • y deja la limpieza de RxJS completamente terminada antes de retirar rxjs-compat.

Eso reduce sustos, evita falsas victorias y te deja una base mucho más limpia para seguir evolucionando la aplicación.

  • angular
  • rxjs
  • firebase