Angular 16, una super versión 🦸‍♂️

Actualizado el 17.07.2023 a las 21:51

Publicado el 07.05.2023 a las 21:51

Angular 16, una super versión 🦸‍♂️

  1. ¿Qué trae nuevo Angular 16 y por qué para mí es una súper versión?

    • ¿Qué son las señales o signals?

    • Hola bindToComponentInputs, adiós ActivatedRoute

    • Añadiendo ficheros environment

  2. ¿Cómo sabemos con qué versión de Angular estamos trabajando?

    • Con el package.json

    • Ejecutando la aplicación

    • Con el CLI

  3. ¿Cómo actualizar/migrar a Angular 16?

  4. Actualizar Angular CLI globalmente

  5. Posibles errores

    • Package '@angular/core' is not a dependency

  6. Cuando todo falla

Logo de fjmduran

Angular 16, una super versión 🦸‍♂️

¿Por qué para mí Angular 16 es una super versión?, ¿Quieres instalar Angular 16?, ¿quieres actualizar o migrar a Angular 16?, ¿quieres saber qué trae esta súper versión de Angular?

¿Qué trae nuevo Angular 16 y por qué para mí es una súper versión?

Sin duda, esta versión para mí es la que más ilusión me hace. Y el motivo fundamental son las señales que se introducen en Angular 16.

¿Qué son las señales o signals?

El primer framework en introducir el patrón signals o las señales en el desarrollo web fue SolidJS.


De forma muy simplificada, piensa en las señales como unas variables en memoria capaces de informar a los objetos que las están consumiendo cuando cambia su valor.


Cuando exista un cambio, se hará sólo en la zona del componente donde se esté consumiendo la señal que ha cambiado, con lo que dotará a Angular 16 de una mayor velocidad al no actualizar todo el árbol de componentes.


Hasta ahora, cuando había un cambio, Angular disparaba un ciclo de detección completo para actualizar todas las propiedades de las clases, se hubiera modificado su valor o no.


Angular va a tratar a las señales como variables primitivas por lo que no dependerá de una clase o de un componente de Angular.


Angular usa some.js como librería de bajo nivel para implementar la detección de cambios disparando el rendimiento la aplicación, pero ¿por qué se dispara el rendimiento de la aplicación?.


Uno de los motivos es porque se eliminan algunas de las API de bajo nivel del navegador como el addEventListener.


Es posible que al principio la sintaxis de una señal te choque un poco, pero cuando comienzas a usarlas ves rápidamente su beneficio.

Personalmente lo noté sobretodo como sustituto del BehaviorSubject de RXJS que usaba en los servicios para tener un gestor de cambios.


No quiero seguir hablando de las señales en Angular, porque quiero escribir un artículo sólo acerca de ello implimentando ejemplos de código.


Por darte una pequeña introducción, te diré que cuando quieras crear una señal será tan fácil como definir una constante y usar la función signal que viene del @angular/core y le asignes un valor inicial

const miPrimeralSeñal = signal('Primer valor de la señal');

La función signal devuelve un valor que nos permite mutarlo o asignarle un nuevo valor.

Hola bindToComponentInputs, adiós ActivatedRoute

Te cuento lo que hacemos ahora mismo para leer un parámetro de una ruta hasta ahora.


Imagina que quieres pasar por la ruta de la aplicación un parámetro id, la ruta sería algo como:

http://localhost:4200/order?id=30227201;

Hasta ahora, para leer el parámetro id de la ruta haríamos algo como:

  //En el archivo de rutas tendremos algo como 👇
  const routes: Routes = [
      {
          path: "order/:id",
          component: OrderComponent,    
      },
  ];
  
  @Component({})
  export class OrderComponent implements OnInit {
      // Inyectamos el ActivateRoute para obtener la información de la ruta
      private route = inject(ActivatedRoute);
              
      query$ = this.route.queryParams.pipe(map((queryParams) => queryParams['id']));
              
      ngOnInit() {
          this.query$.subscribe(query => { 
              // aquí lo que quieras hacer 
              // si quisieras hacer una petición http con el parámetro id tendríamos que usar el switchmap de RXJS
          });
      }
  }
          

Angular 16 nos facilitará este proceso de obtención de parámetros de la url.


En Angular 16 se podría hacer:

  const routes: Routes = [
  {
      path: "order",
      component: OrderComponent,
  },
  ];
            
            
  @Component({})
  export class OrderComponent implements OnInit {
      // Usando la misma URL que antes http://localhost:4200/order?id=30227201
  
      @Input() id?: string; // podemos usar el mismo nombre que el query param
            
      /* 
          O podemos usar un nombre diferente, por ejemplo 'orderId', and then we can use the @Input('orderId')
          Example url: http://localhost:4200/order?id=30227201
      */
      @Input('orderId') queryParam?: string;
            
      ngOnInit() {
          // ejecutar el código que quieras con el orderId
      }
  }
          

Para poder trabajar con Angular 16 de esta última forma lo único que tienes que hacer es activarlo en el RouterModule:

@NgModule({
      imports: [
          RouterModule.forRoot([], {
          //...
  👉      bindToComponentInputs: true // 👈 ACTIVAR
          })
      ],
      })
      export class AppModule {}
          

Y en el caso que sea una aplicación standalone

  bootstrapApplication(App, {
      providers: [
          provideRouter(routes, 
              //...
  👉          withComponentInputBinding() // 👈 ACTIVAR
          )
      ],
      });
      

Resumiendo, ¿cómo harías una migración a la nueva forma?

  // ANTES
  @Component( {})
  export class OrderComponent implements OnInit  {
      private route = inject(ActivatedRoute);
  
      id$ = this.route.params.pipe(map((params) => params['id']));
  
      ngOnInit()  {
          this.id$.subscribe(id =>  { // trabajar con el id });
      }
  }
  
  // DESPUÉS
  @Component( {})
  export class OrderComponent implements OnInit  {
      @Input() id?: string; // vendrá de la ruta
  
      ngOnInit()  {
          // trabajar con el id
      }
  }
      

Añadiendo ficheros environmet

Desde la versión 15, si creas un proyecto de Angular con el CLI ya no te genera los ficheros environment.ts ni environment.prod.ts.


Tendrás que crear una carpeta en la raíz de proyecto que se llame environments y dentro crear los dos ficheros:

  • environment.ts
  • environment.prod.ts

Por último, para que tu proyecto de Angular cargue el fichero de environment.ts cuando estés en desarrollo y el de environment.prod.ts cuando estés en producción añade a tu fichero angular.json en la sección de configuration en la subsección de production:

"fileReplacements": [
    {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.prod.ts"
    }
        ]
Añadiendo environment a Angular

¿Cómo sabemos con qué versión de Angular estamos trabajando?

Podemos ver qué versión de Angular es con la que estamos trabajando de las varias formas 📃

Con el package.json

Abriendo el package.json, en la sección de las dependencias encontraremos el Angular Core y ahí podremos ver la versión de Angular

  "@angular/animations": "~14.0.0",
  "@angular/cdk": "^14.0.3",
  "@angular/common": "~14.0.0",
  "@angular/compiler": "~14.0.0",
  "@angular/core": "~14.0.0"

Ejecutando la aplicación

Ejecutando la aplicación y en con las herramientas de depuración del navegador, podremos ver en la inspección de elementos el ng-version

<app-root _nghost-serverapp-c137="" ng-version="16.0.0">...

Con el CLI

Ejecutando el comando ng version en la consola en el directorio de trabajo, ello te facilitará toda la información de tu proyecto de Angular

¿Cómo actualizar/migrar a Angular 16?

Voy a seguir las recomendaciones del equipo de Angular en su documentación


Para actualizar mi aplicación de Angular 15 a Angular 16 que incluye Angular Material seguiré los siguiente pasos:

  1. ng update @angular/core @angular/cli
  2. ng update @angular/material

Actualizar Angular CLI globalmente

Te recomiendo actualizar tu Angular CLI de manera global y así, cada vez que crees un proyecto nuevo lo harás con la última versión de Angular

    Para actualizar Angular CLI de forma global sigue los siguientes pasos:
  1. Desinstala Angular CLI

    npm uninstall -g @angular/cli
  2. Limpia el cache de npm, y así cuando instalemos la nueva versión de Angular CLI nos aseguramos de que está limpia

    npm cache clean --force
  3. Instala la última versión estable de Angular CLI

    npm i -g @angular/cli
  4. Comprueba que se ha instalado la última versión

    ng version

Puedes encontrar más información acerca de Angular CLI en su página oficial de npm

Posibles errores

Package '@angular/core' is not a dependency

Es posible que no tengas la carpeta node_modules debido a que has clonado el repositorio. Para solventarlo instala las depencias de proyecto con:

npm install

Cuando todo falla

Cuando todo falla, el procedimiento que uso es:

  1. Crear un nuevo proyecto con el Angular CLI actualizado a la versión de Angular que quiero
  2. Eliminar la carpeta node_modules
  3. Eliminar el fichero package-lock.json
  4. Modificar las dependencias de mi proyecto de Angular que quiero migrar copiando las versiones del proyecto creado nuevo
  5. Instalar dependencias con npm install

No es lo más rápido, pero funciona.


Las depencias que te recomiendo para Angular 16 son:

  "dependencies": {
    "@angular/animations": "^16.1.0",
    "@angular/cdk": "^16.1.5",
    "@angular/common": "^16.1.0",
    "@angular/compiler": "^16.1.0",
    "@angular/core": "^16.1.0",
    "@angular/forms": "^16.1.0",
    "@angular/material": "^16.1.5",
    "@angular/platform-browser": "^16.1.0",
    "@angular/platform-browser-dynamic": "^16.1.0",
    "@angular/router": "^16.1.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.4",
    "@angular/cli": "~16.1.4",
    "@angular/compiler-cli": "^16.1.0",
    "@types/jasmine": "~4.3.0",
    "jasmine-core": "~4.6.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "typescript": "~5.1.3"
  }
              

Hasta luego 🖖