Componentes standalone en 🅰

¿Se ha rendido Angular ante React?

29.10.2022 a las 15:31

Componentes standalone en 🅰

⚫ Creando el proyecto

⚫ Editando el main.ts

🟣 Código

⚫ Editando el app.component.ts

🟣 Código

⚫ Limpiando el app.component.html

⚫ Añadiendo estilos con PICO CSS

⚫ Creando nuevos componentes

⚫ Implementando las rutas

🟣 Creando el fichero de rutas

🟣 Cargando el fichero de rutas en la aplicación

🟣 Importando el router en el componente

⚫ Añadiendo funcionalidad al app.component.html

⚫ Añadiendo lazy loading

⚫ Conclusiones

⚫ Repositorio en GitHub

Componentes standalone en 🅰

Angular ya no obliga a tener que usar el patrón modular (NgModules) para crear aplicaciones.


Como te contaba en mi artículo de Angular 14 👇

Actualiza a Angular 14

Cómo migrar nuestros proyectos de Angular 13 a 14

Con esta nueva versión es posible generar aplicaciones sin la necesidad de módulos tal y como se hacen en React, es decir, simplemente nos podremos basar en componentes y rutas.


Para crear aplicaciones en Angular basada sólo en componentes y rutas es necesario que uses Angular a partir de su versión 14 inclusive.

Creando el proyecto

Creamos un proyecto con el Angular CLI como es habitual.


Yo lo llamaré angular-standalone.

ng new angular-standalone

Cuando te pregunte el CLI si deseas rutas, dile que no.


Te explicaré cómo crearlas y añadirlas a la aplicación desde cero.

Editando el main.ts

El archivo main.ts es el encargado del arranque de la aplicación.


Si lo abres verás el siguiente código:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
  

Si te fijas, después de verificar si el entorno es el de producción lo que se hace es utilizar el platformBrowserDynamic que utiliza como parámetro el AppModule.


Como nosotros queremos hacer una aplicación sin módulos, sólo con componentes, eliminamos esa carga y en su lugar utilizamos el método bootstrapApplication que importamos de @angular/platform-browser.


❌ Elimina el app.module.ts, no lo vamos a necesitar.


El método bootstrapApplication tiene como argumento requerido un rootComponent que será el componente-contenedor de la aplicación, es decir, el app.component.ts

Código

Hasta aquí tu fichero main.ts se debería ver así:

import { enableProdMode } from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser'
import { AppComponent } from './app/app.component';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

bootstrapApplication(AppComponent)
  .catch(err => console.error(err));
  

No obstante, tendrás un error porque el método bootstrapApplication admite sólo componentes standalone.


Para eliminar el error sigue el siguiente paso.

Editando el app.component.ts

Debemos decirle a Angular que queremos usar este componente como un componente independiente, eso lo hacemos añadiendo al decorador del componente la propiedad de standalone a true.

Código

Tu fichero app.component.ts debería quedar así:

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

@Component({
  standalone:true,
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-standalone';
}
  

Limpiando el app.component.html

Ve al fichero app.component.html y elimina todo el código que Angular tiene por defecto, deja sólo un h1 con el texto Aplicación Angular por componentes

Añadiendo estilos con PICO CSS

Para que la aplicación no se vea fea voy a añadir el framework de pico CSS.


Este paso es totalmente opcional, sólo es para que la aplicación se vea un poco mejor 😅


Para ello tienes diferentes opciones, en esta ocasión lo que voy a hacer es añadirlo como un link de CDN al fichero index.html.


Añade en el head de tu index.html:

  <link rel="stylesheet" href="css/pico.min.css">
  

Creando nuevos componentes

Para demostrarte cómo trabaja Angular con las rutas en los componentes standalone crearemos tres nuevos componentes, para ello ejecuta en tu consola:

          
ng g c firstPage --standalone --inline-template --inline-style --skip-tests
ng g c firstPage --standalone --inline-template --inline-style --skip-tests
ng g c firstPage --standalone --inline-template --inline-style --skip-tests
  
        

Añado el flag standalone para que me añada la propiedad de standalone a true en el decorador del componente.


Añado el flag inline-template para que no me cree fichero html del componente.


Añado el flag inline-style para que no me cree fichero css del componente.


Por último añado el flag de skip-tests para que no me cree el fichero de test.


Comprobamos que en el decorador de cada uno de los componentes se ha añadido el campo de standalone.


Si abres por ejemplo el componente firsPage.component.ts deberías ver lo siguiente:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-first-page',
  standalone: true,
  imports: [CommonModule],
  template: '
    <p>
      first-page works!
    </p>
  ',
  styles: [
  ]
})
export class FirstPageComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}
  

Si te fijas, después de la propiedad de standalone:true importa el CommonModule.


Esto es para hacer uso de las funciones de Angular como por ejemplo las directivas estructurales ngIf, ngFor..., si no los vas a usar puedes eliminar esa importación y así tu componente será más ligero.

Implementando las rutas

Creando el fichero de rutas

En la carpeta src de nuestro proyecto crearemos un fichero llamado app.routing.ts.


En este fichero exportaremos un array de tipo Route importado de @angular/route que básicamente serán las rutas.

import { Route } from '@angular/router';
import { FirstPageComponent } from './first-page/first-page.component';
import { SecondPageComponent } from './second-page/second-page.component';
import { ThirdPageComponent } from './third-page/third-page.component';

export const routes: Route[] = [
  {
    path: 'first',
    component: FirstPageComponent,
  },
  {
    path: 'second',
    component: SecondPageComponent,
  },
  {
    path: 'third',
    component: ThirdPageComponent,
  },
];
  

Cargando el fichero de rutas en la aplicación

El fichero de las rutas lo cargaremos en el main.ts


Cargaremos las rutas dentro del argumento del provideRouter que se importa del angular/router.

Tu fichero main.ts debería quedar así:

import { enableProdMode } from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser'
import { AppComponent } from './app/app.component';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

bootstrapApplication(AppComponent)
  .catch(err => console.error(err));
  

Importando el router en el componente

En un proyecto estándar de Angular, nosotros inyectábamos el router en el app.module.ts, pero ahora ya no tenemos ese fichero.


¿Dónde cargo el router 🤔?


En el app.component.ts como propiedad en el decorador.


Tu app.component.ts se debería ver así:

import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routing';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

bootstrapApplication(AppComponent, {
  providers: provideRouter(routes),
}).catch((err) => console.error(err));
  

Añadiendo funcionalidad al app.component.html

El componente app.component será mi componente principal, puedes llamarlo también como componente contenedor.


Por hacer una analogía con React, sería el app.jsx en React.


Añado tres botones para navegar a cada uno de los componentes que creamos.


Y muy importante, no se nos puede olvidar añadir la directiva router-outlet para renderizar los componentes.

Resumiendo, tu app.component.html debería quedar así:

<div class="wrapper-app">
  <button [routerLink]="['/','first']">First component</button>
  <button [routerLink]="['/','second']">Second component</button>
  <button [routerLink]="['/','third']">Third component</button>
  <router-outlet></router-outlet>
</div>
  

Añadiendo lazy loading

Si no sabes qué es el lazy loading o la carga diferida te dejo un artículo dónde lo explico 👇


Lazy loading en Angular

En este artículo te cuento cómo realizar lazy load o carga asíncrona, diferida o perezosa con Angular


Para añadir lazy loading, es necesario actualizar el fichero del routing app.routing.ts.


En lugar de cargar directamente el componente, cargaremos una función en el campo loadComponent del componente deseado.


Imagina que queremos cargar con lazy loading sólo el componente second, pues el fichero de routing quedaría así 👇

import { Route } from '@angular/router';
import { FirstPageComponent } from './first-page/first-page.component';
import { ThirdPageComponent } from './third-page/third-page.component';

export const routes: Route[] = [
  {
    path: 'first',
    component: FirstPageComponent,
  },
  {
    path: 'second',
    loadComponent: () =>
      import('./second-page/second-page.component').then(
        (component) => component.SecondPageComponent
      ),
  },
  {
    path: 'third',
    component: ThirdPageComponent,
  },
];
  

Conclusiones

Está claro que esto es una apuesta del equipo de Angular por acercarse más a los desarrolladores de React y a los desarrolladores que criticaban que Angular no era para aplicaciones pequeñas porque metía demasiada arquitectura.


Yo sin embargo, pienso que el que la aplicación esté organizada en módulos no es tan incómodo, quiero decir, no me incomoda antes de crear un bloque de la aplicación el crearme el módulo y después ir metiendo todos los componentes.


Personalmente, si uso los componentes standalone probablemente sea para usos donde el componente vaya a tener poca lógica, no sé, el tiempo lo dirá.


Sea como sea, me alegra ver que Google sigue apostando por Angular añadiéndole funcionalidades 🎉

Repositorio en GitHub

Te dejo todo el código en este repositorio


Hasta luego 🖖

Servicios

Software

IoT

Digitalización

Aplicaciones móviles

Consultoría

fjmduran.com v0.1.2