Skip to content

Instantly share code, notes, and snippets.

@mugan86
Last active January 20, 2022 13:26
Show Gist options
  • Save mugan86/4f1703e53157cca2301cd9113723be88 to your computer and use it in GitHub Desktop.
Save mugan86/4f1703e53157cca2301cd9113723be88 to your computer and use it in GitHub Desktop.
Pasos a seguir con Webcomponents en Angular

0.- ¿Qué es un Web Componente?

Hace ya tiempo que los desarrolladores utilizan marcos de trabajo o frameworks de JavaScript como, por ejemplo, React o Angular, para definir elementos web que puedan reutilizar. Sin embargo, cada framework utiliza un estándar diferente, lo cual impide en muchos casos que se puedan aplicar los llamados code snippets o fragmentos de código, que resultan muy útiles por ser comunes a diferentes proyectos. Por suerte, existen los llamados web components (componentes web): elementos HTML reutilizables y compatibles con todos los marcos de trabajo. Estandarizado en 2012, este tipo de elementos web ya puede usarse en todos los navegadores más populares.

Los componentes web son bloques de código que encapsulan la estructura interna de elementos HTML, incluyendo CSS y JavaScript, permitiendo así que el código se pueda volver a usar como se quiera en otras webs y aplicaciones. El concepto fue desarrollado por un grupo de trabajo del World Wide Web Consortium (W3C), creado en 1994 por Tim Berners-Lee, considerado el padre de la Web. Este grupo trabaja desde entonces por la estandarización de todas las tecnologías básicas de la red. El modelo de componentes web que se publicó en 2012 establece principalmente cuatro especificaciones referidas a la creación de estos prácticos elementos HTML. Son las siguientes:

Custom elements: conjuntos de API de JavaScript para definir elementos personalizados por el usuario. Shadow DOM: conjunto de API de JavaScript para añadir elementos DOM. ES Modules: módulos para integrar y reutilizar documentos de JavaScript. HTML templates: plantillas HTML que no se muestran en la página web final y que pueden servir de base para ciertos elementos definidos por el usuario. Todos los navegadores convencionales ya son compatibles con los componentes web estándares. Para trabajar con los códigos HTML encapsulados, pueden usarse todos los frameworks o bibliotecas de JavaScript que trabajan con HTML.

1.- Tener Angular CLI instalado

2.- Proyecto Creado y listo para empezar (probamos con npm start)

3.- Instalar y configurar Angular Elements

(https://angular.io/guide/elements)

  • Si da error Uncaught TypeError with the message: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function. tsconfig.json => target 2015

  • Ahora usamos el componente principal, que será la base que usaremos para pintar la información del servicio que vamos a crear

  • Creamos un servicio, donde vamos a consumir la información de la API de Ergast MRD F1, para obtener los últimos resultados de la última carrera ng generate service data --skip-tests

  • Añadir HttpClientModule en el app.module.ts

  • https://ergast.com/mrd/

  • Ir a últimos resultados

...
  constructor(private httpClient: HttpClient) { }

  get(): Observable<any>{
    return this.httpClient.get(`https://ergast.com/api/f1/current/last/results.json`);
  }
}
  • Crear nuevo componente ng g c last-results --skip-tests

  • Inyectar el servicio

  • Ahora, con la respuesta del servicio, vamos a definir las interfaces de los elementos de la respuesta sin esfuerzo: http://json2ts.com/

  • Copiar la respuesta, convertir a TS, y llevarlo a la interface.

  • En el componente

export class LastResultsComponent implements OnInit {
  results!: Array<Result>;
  race!: string;
  constructor(private dataService: DataService) { }

  ngOnInit(): void {
    this.dataService.get().subscribe((data: LastResults) => {
      console.log(data);
      this.race = data.MRData.RaceTable.Races[0].raceName;
      this.results = data.MRData.RaceTable.Races[0].Results;
      console.log(this.results);
    });
  }

}
  • Ahora pintamos en el HTML:
<h2>Último Gran Premio: {{race}}</h2>
<div *ngFor="let result of results">
    <h5>{{result.Driver.givenName}} {{result.Driver.familyName}}</h5>
    <p>
        Salida: {{result.grid}} / Final: {{ result.position }}
    </p>
    <a href="{{result.Driver.url}}">Más detalles</a>
</div>
  • Ya tenemos el mínimo para poder crear el web component.

Transformando el componente Angular a Web Component

  • Vamos a app.module.ts y añadimos el componente de los últimos resultados en la propiedad
import { Injector} from '@angular/core';
import { createCustomElement } from '@angular/elements';

@NgModule({
 ...
  bootstrap: [AppComponent, LastResultsComponent] <====
})
// Inyectar "Injector"
export class AppModule {
      constructor(private injector: Injector) {
        const el = createCustomElement(LastResultsComponent, { injector });
        customElements.define('f1-last-race-results-widget', el);
      }
      ngDoBootstrap(): void {}
}
  • Crear el distribuible, con los nombres de ficheros sin hashing
ng build --prod --output-hashing none
  • Tenemos en dist/proyecto
  • index.html, cambiamos el valor del de la etiqueta por la que hemos dicho al crear el web component
<f1-last-race-results-widget></f1-last-race-results-widget>
  • Instalamos para tener nuestro servidor local
sudo npm install -g serve
  • Ahora nos ubicamos en el directorio donde se ha generado el código:
serve

Llegados a este punto ya tendríamos todo lo necesario, pero podemos crear el webcomponent en un fichero. (Pasamos al script)

npm install --save-dev concat fs-extra
// Añadir en el package.json
// "build:component": "ng build --prod --output-hashing none && node build-web-cp.js"
const fs = require('fs-extra');
const concat = require('concat');
build = async () =>{
const files = [
'./dist/web-comp-ng/runtime.js',
'./dist/web-comp-ng/polyfills.js',
'./dist/web-comp-ng/main.js'
];
await fs.ensureDir('widget');
await concat(files, 'widget/f1-races-results.js');
}
build();
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebCompNg</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<body>
<widget></widget>
<script src="./web-comp-ng/widget/f1-races-results.js" defer></script></body>
</html>