General nerdness

Ivan Reyes – Trying to blag is never easy, that's why I have no posts xD

Custom Elements or How I Learned to Stop Worrying and Love the Web Components

Oct 09, 2019


Custom Car

Un web component es una forma de crear tu propio HTML Custom Element que puede ser utilizado en cualquier sitio web y en la mayoría de los browsers. Su funcionalidad esta encapsulada del resto de la página y eso quiere decir que puedes también utilziar los web components de alguien más en tu propio sitio. Hoy vamos a crear un Web Component utilizando Angular Elements.

Para entender los web components hay que hablar de Polyfills, el Shadow DOM, HTML5 y exactamente qué es un Custom Element en términos de Javascript. Afortunadamente para usarlos no necesitas entender mucho de ninguna de esas cosas :laughing:

Angular Elements

Angular Elements nos permite empaquetar cualquier Angular Component como un web component. El resultante lo podemos utilizar en cualquier lugar independientemente del resto de la plataforma de Angular.

La ventaja de crear un web component con Angular es que angular se va a encargar de crear los polyfills que necesitamos, vamos a tener acceso a todas las librerías y podemos utilizar Typescript, lo cual siempre es bueno y bonito.

Vamos a empezar con un proyecto nuevo. Si tienes dudas de cómo empezar un proyecto más “tradicional” puedes ver la guía anterior.

ng new tesel-ce --style scss
cd tesel-ce
ng add @angular/elements

En este caso solo vamos a modificar el componente principal src/app/app.component.ts

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

@Component({

  //...

  encapsulation: ViewEncapsulation.ShadowDom
})
export class AppComponent { }

La parte importante es el encapsulation: ViewEncapsulation.ShadowDom Shadow DOM significa que nuestro componente va a estar aislado del resto del documento HTML dónde lo vamos a integrar. En el sentido práctico quiere decir que puedes usar cualquier class, id, name, css, etc. sin preocuparte de que interfiera con el resto de la página y viceversa.

En src/app/app.component.html voy a colocar uno de los ejemplos de Bootstrap, porque quiero mostrar cómo el Shadow DOM aisla el componente.

<div class="card" style="width: 18rem;">
  <img src="https://picsum.photos/400" class="card-img-top">
  <div class="card-body">
    <h5 class="card-title">Hello World!</h5>
    <p class="card-text">Ejemplo de cómo el Shadow DOM nos permite aislar el component.</p>
  </div>
  <ul class="list-group list-group-flush">
    <li class="list-group-item">Cras justo odio</li>
  </ul>
  <div class="card-body">
    <a href="#" class="card-link">Card link</a>
  </div>
</div>

Finalmente hay que modificar src/app/app.module.ts. Nuestro componente ya no a ser un componente bootstrap si no que es un entryComponent. Esto nos permite registrarlo como un Custom Element

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from "@angular/elements";

import { AppComponent } from './app.component';

@NgModule({

  //...

  entryComponents: [AppComponent] //  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    let custom = createCustomElement(AppComponent, { injector: this.injector });
    customElements.define("custom-example", custom);
  }
}

Al agregar @angular/elements tenemos acceso al método createCustomElement que le permite al DOM reconocer en qué momento se esta mandando a llamar nuestro propio custom element.

Lo único que queda es compilar nuestro componente.

ng build --prod --output-hashing=none
cat dist/tesel-ce/{runtime,polyfills,main}-es5.js > tesel-ce.js

Lo compilamos en modo de producción porque no hay comments y minimiza el resultado entre otras cosas; lo compilamos sin hashes para hacer más fácil poner nuestro runtime-es5.js, polyfills-es5.js y main-es5.js en el mismo archivo más fácilmente.

La prueba

Voy a crear un index.html en cuyo body solo hay estas lineas, y voy a agregar bootstrap para mostrar el efecto del Shadow DOM.

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>
    <custom-example></custom-example>
    <script src="tesel-ce.js"></script>
  </body>
</html>

Si abres este index en tu browser vas a notar que se despliega todo lo que creamos antes aunque no hay ningún CSS. Esto se debe al Shadow DOM!

Angular Element aislado por el Shadow DOM

Mientras que si colocas el html del custom element en el index.html se va a mostrar así.

HTML nativo mostrando su CSS

Una vez más, esto te permite utilizar cualquier clase, estilos y nombres que quieras sin preocuparte.


Eso es todo en este post, espero que te ayude a empezar a probar los web elements. Si quieres saber más sobre Angular puedes revisar este post. Y si quieres leer algo más entretenido puedes leer mi post acerca de dados.

– Ivan Reyes