Angular Element example: add Angular components to an existing Web page

Updated: 2024-01-11

This post has 2 main goals and 1 bonus goal.

  • see RxJS in action with real code
  • integrate an Angular Element (Web Component) in a static page
  • (bonus) show that we can build videogames with Angular and Canvas HTML :D (maybe!)

The simulator on this page is built with Angular and uses 'real' RxJS operators to generate the animations.

rxjs simulator

This is an Angular element integrated into a static webpage, originally in markdown converted in HTML using Spring and Mustache (no Angular or JS).

The original post of 2022 has been updated, but the app is still in Angular 13.

On this page

Add Angular components to an existing webpage (not an Angular App)

Angular introduced Angular Elements many versions ago ;-), these are Components packaged in Web Components. Web components can be integrated, in theory, into existing non-Angular WebApps and Websites.

In my personal opinion, this feature is underrated. I mostly see, in real-world projects, integrations using Stencil.js. I think that the Angular team didn't receive an enthusiastic feedback either and this feature didn't receive relevant refreshes in the more recent versions.

In the example that you can see on this page, the Angular component that generates the simulation is included in a Markdown page that Java and Mustache transform in a classic HTML page.

The Angular Element is called using:

<div style="max-width: 1000px"> 
  <marble-element></marble-element> 
</div> 

the dependencies of the page are:

<link rel="stylesheet" href="/assets/webcomponents/ng/marbles/styles.css"> 
<script src="/assets/webcomponents/ng/marbles/runtime.js" type="module"></script> 
<script src="/assets/webcomponents/ng/marbles/polyfills.js" type="module"></script> 
<script src="/assets/webcomponents/ng/marbles/main.js" type="module"></script> 

we use type=module to delay the loading of the components.

DEMO: RxJS examples (click on the list!)

There are always questions about RxJS. Ouch!

It's not very funny to study the official documentation, to prepare an interview I created the simulator that you can find here.

You can start it by clicking on the operator title that you want to see.

5 numbers and 5 letters are sent as Observables to the RxJS operator. The result is shown in the lower third.

If you click on 'Stop subscription' the marbles stop moving and you can explain what is happening. If you click on an operator name you restart the procedure.

I will add other operators in the future.

Issues

  • please note that the color of the marbles and their timing are random, if there are white marbles these won't be visible.
  • if 2 marbles appear at the same instant one will be hidden.

Deep dive in the Angular Element / Web Component

The component was built with Angular 13, so it is still using modules.

The main action is inside a graphical canvas ... I don't know if this means that you can build games with Angular ;-)

<div class="flex-container" style="margin-top: 20px"> 
  <div> 
    <canvas #myCanvas class="my-canvas"></canvas> 
    <button (click)="stopSubscriptions()">Stop subscription</button> 
  </div> 
  <div> 
    <ul class="list-group"> 
      <li *ngFor="let operator of operators" class="list-group-item select-element" (click)="selectOperator(operator)"> 
        <span class="title-text">{{operator.name}}</span><p>{{operator.description}}</p> 
      </li> 
    </ul> 
  </div> 
</div> 

The marble module is the following

import {createCustomElement} from "@angular/elements"; 
 
@NgModule({ 
  declarations: [ CanvasComponent, OperatorDescriptionComponent ], 
  imports: [ CommonModule, BrowserModule ], 
  providers: [], 
  exports: [ CanvasComponent, OperatorDescriptionComponent ] 
}) 
export class MarblesModule { 
  constructor(private injector: Injector) { 
    // we need to create a custom element 
    const el = createCustomElement(CanvasComponent, {injector}); 
    // we register the component as DOM element 
    customElements.define('marble-element', el); 
  } 
 
  // web bootstap the custom element 
  ngDoBootstrap(){} 
  /* Now, we can run the command ng build & ng package and finally we will run ng serve to serve the dist/ folder generated using the build command. 
   Also, we can use the gzip obtained from the ng package command, extract it and publish it as an npm module.*/ 
} 

We use the function createCustomElement that comes with @angular/elements: documentation

In the package script we generate a zipped files that will contain the elements to include in our page:

"scripts": { 
  "ng": "ng", 
  "start": "ng serve", 
  "build": "ng build --output-hashing=node", 
  "package": "cat dist/marble/{runtime,polyfills,scripts,main}.js | gzip > my-custom-element.js.gz", 
}, 

You can see the HTML code added on this webpage (you can check the browser console):

html code

How big is this component?

The transferred size is less than 70 kb, this amount doesn't add a lot to a modern webpage. I didn't check with a more recent version of Angular, I think it could be easily optimized.

runtime

Where is the code?

The code is in my GitHub repository :D, if you want me to publish let me know. It's currently in private mode. Probably because I quickly created it years ago for a Demo and it's not clean (at all).


You could be interested in

Right click context menu with Angular

Right click custom menu inside dynamic lists with Angular Material
2020-05-31

Enums in Angular templates

How to use enum in the html template
2019-01-21
Fullstack Angular / Java application quick start guide.
WebApp built by Marco using SpringBoot 3.2.4 and Java 21. Hosted in Switzerland (GE8).