Cuando convertirmos una aplicación de Angular con Angular Elements, si esta aplicación tiene un tamaño algo mayor, tendremos secciones. ¿Cambia mucho convertir con o sin routing? ¿Qué poblemas nos podemos encontrar?
Vamos a partir de que hemos leído los anteriores artículos de Angular Elements publicados. Además tenemos en un respositorio publicado, las aplicaciones completas con la documentación.
Tendremos una aplicación central llamada Portal, donde importaremos el angular elements generado, y una aplicación para crear el angular elements con routing.
Generamos SPA-With-Routing
Generamos la aplicación spa-with-routing, donde añadiremos las rutas.
ng new spa-with-routing --routing --prefix=spa-with-routing
Pasos
Añadimos la dependencia de Angular Elements
ng add @angular/elements
Esto nos añadirá algunas modificaciones en el proyecto, además de la dependencia en el package.json
"@angular/elements": "^9.1.9",
Instalamos librería para compilar
Instalamos la librería que utilizaremos para compilar el proyecto en Angular Elements. La instalaremos en formato general, pero también podríamos instalarla en uno de los subproyectos.
ng add ngx-build-plus
Actualizamos app.module.ts, para registrar nuestro componente como un custom element. Además eliminaremos el componente de la propiedad Bootstrap, dado que lo ejecturemos a través del ngDoBoostrap.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: []
})
export class AppModule {
constructor(private injector: Injector) {
const el = createCustomElement(AppComponent, { injector });
customElements.define('spa-one-elements', el);
}
ngDoBootstrap() {}
}
Actualizamos el package.json, para generar el build. Con este comando conseguiremos tener solo un build, además de quitar los hash de los bundldes.
"build:ele": "ng build --prod --output-hashing none --single-bundle true",
Una vez generado, nos vamos con la terminal a la carpeta ./dist/spa-one y actualizamos el contenido para añadir el custom-element, en lugar del app-root
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>SpaOne</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<spa-with-routing-elements></spa-with-routing-elements>
<script src="polyfills-es5.js" nomodule defer></script>
<script src="polyfills-es2015.js" type="module"></script>
<script src="main-es2015.js" type="module"></script>
<script src="main-es5.js" nomodule defer></script>
</body>
</html>
Luego, en la terminal, levantamos un servidor temporal, para comprobar su visualización. Luego abrimos nuestro navegador en la ruta http://localhost:9080/
.
npx static-server
En el caso de que quisiésemos un webcomponent, deberíamos añadir una propiedad al app.component.ts para añadirle el shadown.
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'spa-with-routing-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class AppComponent {
title = 'spa with routing';
}
Como podemos ver en la captura de pantalla, si vamos al depurador, veremos que ahora tenemos #shadow-root.
Crear routing
Generaremos dos componentes.
ng g component pages/home
ng g component pages/contact
Creamos un menú en el app.component.html
Esto es una SPA básica con routing sin lazy loading
<ul>
<li><a routerLink="home">home</a></li>
<li><a routerLink=contact">contact</a></li>
</ul>
<router-outlet></router-outlet>
Actualizamos el routing
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './pages/home/home.component';
import { ContactComponent } from './pages/contact/contact.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'contact', component: ContactComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Una vez probada la aplicación, y viendo que todo funciona correctamente. Pasaríamos a generar el build en formato angular elements.
Una vez generado este build, para su consumo podemos:
- Importarlo directamente en un proyecto
- Subir el desplegado a un repositorio para su importación como dependencia
- Desplegarlo en un repositorio de binarios, para su importación como dependencia
Spa With Routing en el Portal
Aplicación convertida en angular Elements con secciones
Pasos
Añadimos la dependencia de Angular Elements
ng add @angular/elements
Esto nos añadirá algunas modificaciones en el proyecto, además de la dependencia en el package.json
"@angular/elements": "^9.1.9",
Instalamos librería para compilar
Instalamos la librería que utilizaremos para compilar el proyecto en Angular Elements. La instalaremos en formato general, pero también podríamos instalarla en uno de los subproyectos.
ng add ngx-build-plus
Actualizamos app.module.ts, para registrar nuestro componente como un custom element. Además eliminaremos el componente de la propiedad Bootstrap, dado que lo ejecturemos a través del ngDoBoostrap.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: []
})
export class AppModule {
constructor(private injector: Injector) {
const el = createCustomElement(AppComponent, { injector });
customElements.define('spa-one-elements', el);
}
ngDoBootstrap() {}
}
Actualizamos el package.json, para generar el build. Con este comando conseguiremos tener solo un build, además de quitar los hash de los bundldes.
"build:ele": "ng build --prod --output-hashing none --single-bundle true",
Una vez generado, nos vamos con la terminal a la carpeta ./dist/spa-one y actualizamos el contenido para añadir el custom-element, en lugar del app-root
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>SpaOne</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<spa-with-routing-elements></spa-with-routing-elements>
<script src="polyfills-es5.js" nomodule defer></script>
<script src="polyfills-es2015.js" type="module"></script>
<script src="main-es2015.js" type="module"></script>
<script src="main-es5.js" nomodule defer></script>
</body>
</html>
Luego, en la terminal, levantamos un servidor temporal, para comprobar su visualización. Luego abrimos nuestro navegador en la ruta http://localhost:9080/
.
npx static-server
En el caso de que quisiésemos un webcomponent, deberíamos añadir una propiedad al app.component.ts para añadirle el shadown.
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'spa-with-routing-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class AppComponent {
title = 'spa with routing';
}
Como podemos ver en la captura de pantalla, si vamos al depurador, veremos que ahora tenemos #shadow-root.
Crear routing
Generaremos dos componentes.
ng g component pages/home
ng g component pages/contact
Creamos un menú en el app.component.html
Esto es una SPA básica con routing sin lazy loading
<ul>
<li><a routerLink="home">home</a></li>
<li><a routerLink=contact">contact</a></li>
</ul>
<router-outlet></router-outlet>
Actualizamos el routing
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './pages/home/home.component';
import { ContactComponent } from './pages/contact/contact.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'contact', component: ContactComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Una vez probada la aplicación, y viendo que todo funciona correctamente. Pasaríamos a generar el build en formato angular elements.
Una vez generado este build, para su consumo podemos:
- Importarlo directamente en un proyecto
- Subir el desplegado a un repositorio para su importación como dependencia
- Desplegarlo en un repositorio de binarios, para su importación como dependencia