Actualiza a Angular 14
Cómo migrar nuestros proyectos de Angular 13 a 14
Publicado el 29.10.2022 a las 13:31
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.
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.
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
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.
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.
Tu fichero app.component.ts debería quedar así:
import { Component, inject } from '@angular/core'; @Component({ standalone:true, selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], standalone: true, imports: [ToolbarComponent,FooterComponent,HeaderArticleComponent,LoaderComponent,ArticlesBannerComponent], }) export class AppComponent { title = 'angular-standalone'; }
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
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">
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 secondPage --standalone --inline-template --inline-style --skip-tests ng g c thirdPage --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.
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, }, ];
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));
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));
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>
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, }, ];
Actualización 26.03.2023
Para que cada vez que generes un nuevo componente te ahorres el escribir el flag standalone cuando lo deseas standalone con Angular CLI lo que tienes que hacer es:
Te debe de quedar algo así:
"projectType": "application", "schematics": { "@schematics/angular:component": { "style":"scss", "standalone": true 👈 } }, "root": "",
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 🎉
Te dejo todo el código en este repositorio
Hasta luego 🖖