Angular Elements desde todos los ángulos 1

Los webcomponents se hicieron famosos por la posibilidad de reutilizar componentes de diferentes tecnologías y versiones. Pero en muchos proyectos han ampliado su alcance, haciendo proyectos reutilizables para importarlos. ¿Qué nos permite Angular Elements?

¿Qué es Angular Elements?

Angular elements are Angular components packaged as custom elements, a web standard for defining new HTML elements in a framework-agnostic way

Resumiendo, es para crear componentes reutilizables aplicando Shadow Dom. Aunque hoy en día, también se utiliza para conseguir proyectos aislados para importar en otros proyectos. Algo desaconsejado por la documentación, pero una alternativa para no utilizar iframes.

En estos proyectos vamos a analizar todas las casuísticas posibles con Angular Elements, partiendo de la versión de Angular 9.

Tendremos diferentes proyectos, aunque partiremos siempre del proyecto portal, que será el proyecto principal.

Antes de entrar a trabajar con Angular Elements, es aconsejable que leáis un poco de documentación de que son los webcomponents.

Programas

Para llevar a cabo las siguientes fases, deberéis tener instalado:

Versiones

versiones

Pasos que vamos a realizar

Generar estructura proyecto

mkdir angular-elements-9
cd angular-elements-9

Inicializamos el git

git init

Creamos nuestra aplicación de angular y elegimos sass => scss

npx ng new portal ---routing --prefix=portal

versiones

Nos metemos dentro del proyecto

cd portal

Instalamos las dependencias, con npm o yarn

yarn install
// o
npm install

Probamos que la aplicación funciona correctamente

npm run start

Abrimos el navegador, y ponemos la ruta que nos indica la terminal: http://localhost:4200/

Podemos ver la aplicación de angular ejecutada.

versiones

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»,

Llegado a este punto, ya tenemos nuestra aplicación principal configurada, para ir creando los angular element y posteriormente importarlos.

Existen muchas formas de utilización de angular elements en un proyecto. En este caso, para hacerlo más sencillo, vamos a copiar las carpetas dist de cada proyecto spa-numero en la carpeta assets de nuestro proyecto portal, y los imporemos de ahí. Algunas de las otras posibilidades serían mediante dependencias de package.json, CDN, por configuración, etc…

Vamos a crear un menú con lazy loading para ir añadiendo las diferentes SPA de webcomponents que vayamos generando.

Desde la aplicación portal, lanzamos el siguiente comando:

ng g module pages/section-spa-one --route section-spa-one --module app.module

Este comando nos generará un module con su componente respectivo, donde luego importaremos el webcomponent generado de SPA One.

Luego en de app.component.html, añadimos el menú, para que tengamos la opción de routing.

Portal

Toda la documentación del portal y pasos realizados para su generación, están en su propio readme.

Generamos SPA-ONE

Generamos la aplicación spa-one

ng new spa-one --routing --prefix=spa-one

La documentación de dicha aplicación, y como generarla, está en su propio readme.

Vamos a convertir esta aplicación Spa One en un webcomponent que importaremos desde la aplicación Portal.

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.

También tenemos la opción de generar un script para concatenar todos los JS, pero esta librería nos permitirá configuraciones avanzadas, que en otros artículos analizaremos.

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 { 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 app.component.ts, para convertir nuestro custom element en un webcomponent, al aplicarle el shadow dom.

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

@Component({
  selector: 'spa-one-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsultion.Native
})
export class AppComponent {
  title = 'spa-one';
}

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-one-elements></spa-one-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

Custom Element

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-one-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.ShadowDom
})
export class AppComponent {
  title = 'spa-one';
}

Como podemos ver en la captura de pantalla, si vamos al depurador, veremos que ahora tenemos #shadow-root. 

Webcomponent

Ya tenemos la gran parte terminada. Ahora solo nos faltaría importar nuestro proyecto webcomponent en el portal.

Importando webcomponent

Deberemos copiar la carpeta dist/spa-one de nuestro proyecto, y llevarla a assets del proyecto portal.

La forma básica de añadir un webcomponent en nuestra aplicación, será importándolo en el componente de nuestra aplicación donde queramos mostrarlo y añadir la etiqueta html.

// section-spa-one.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';

import { SectionSpaOneRoutingModule } from './section-spa-one-routing.module';
import { SectionSpaOneComponent } from './section-spa-one.component';

import '../../../assets/spa-one/main-es2015';

@NgModule({
  declarations: [SectionSpaOneComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  imports: [CommonModule, SectionSpaOneRoutingModule],
})
export class SectionSpaOneModule {}
// section-spa-one.component.html
<p>section-spa-one works!</p>
<spa-one-elements></spa-one-elements>

Ventajas:

  • Fácil de aplicar
  • Rápido

Desventajas

  • No podemos controlar si hay algún error en el webcomponent
  • No podemos gestionar su carga
  • No podemos generarlo dinámicamente

 

 

Deja un comentario