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.
(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
-
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.
- 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
https://developer.mozilla.org/es/docs/Web/Web_Components
https://angular.io/guide/elements
https://angular.io/cli
https://angular.io/cli/new
https://www.npmjs.com/package/@angular/elements
http://ergast.com/mrd/
https://ergast.com/api/f1/current/last/results
https://ergast.com/api/f1/current/last/results.json
http://json2ts.com/
https://www.npmjs.com/package/serve