#angular #web-components #elements #shadow-dom

Day 24 — Angular Elements: Exporting to Web Components

📘 Day 24 — Angular Elements: Exporting to Web Components

Zero to Hero — Hands-on Angular Tutorial

Today you will learn:

  • ✔️ What are Web Components (Custom Elements)?
  • ✔️ Why use Angular Elements?
  • ✔️ Installing @angular/elements
  • ✔️ Converting an Angular Component to a Custom Element
  • ✔️ Building a standalone JS script (for use in React, Vue, or WordPress)

Usually, Angular components only work inside Angular. Today, we break those walls. You will create a component that works anywhere (even in a plain .html file). 🧱


🟦 1. What are Angular Elements?

Angular Elements allows you to package your Angular component as a standard Web Component (<my-widget>).

Capabilities:

  • Works in React, Vue, Svelte, or vanilla JS.
  • Self-contained (encapsulates styles and logic).
  • Can be lazy loaded by scripts.

Use Case: Embedding a complex “Calculator” or “Chat Widget” into a legacy CMS or a partner’s website.


🟩 2. Setup

Install the package:

ng add @angular/elements

🟧 3. Create the Component

Let’s build a simple “Like Button” that increments a counter.

like-button.component.ts:

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

@Component({
  selector: 'app-like-button',
  standalone: true,
  template: `
    <button (click)="like()">
      👍 {{ count }} Likes
    </button>
  `,
  styles: [`
    button { background: #007bff; color: white; padding: 10px; border: none; border-radius: 5px; cursor: pointer; }
  `],
  // Important: Use ShadowDom to prevent styles from bleeding out/in!
  encapsulation: ViewEncapsulation.ShadowDom 
})
export class LikeButtonComponent {
  @Input() count = 0;
  @Output() liked = new EventEmitter<number>();

  like() {
    this.count++;
    this.liked.emit(this.count);
  }
}

🟥 4. Register as a Custom Element

Modifying app.config.ts (or main.ts) to tell the browser about this new tag.

app.component.ts (or main.ts):

import { Component, Injector, OnInit } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { LikeButtonComponent } from './like-button/like-button.component';

@Component({ ... })
export class AppComponent implements OnInit {
  
  constructor(private injector: Injector) {}

  ngOnInit() {
    // 1. Convert Angular Component to Custom Element Class
    const Element = createCustomElement(LikeButtonComponent, { injector: this.injector });

    // 2. Register it with the Browser (window.customElements)
    customElements.define('my-like-button', Element);
  }
}

Now, you can use it in index.html:

<!-- Outside of <app-root>! -->
<my-like-button count="10"></my-like-button>

<script>
  const btn = document.querySelector('my-like-button');
  btn.addEventListener('liked', (e) => console.log('New Count:', e.detail));
</script>

🟫 5. Building a Standalone Script file

If you want to give this file to a React developer, you need a single .js file.

  1. Build the app:

    ng build --output-hashing=none
  2. Concatenate the output files (polyfill, main, runtime) into one. You can use a simple script or just manually import them.

    Usually, the output in dist/ contains:

    • main.js
    • polyfills.js
    • styles.css
  3. Embed in external site:

    <html>
      <head>
        <link rel="stylesheet" href="styles.css">
      </head>
      <body>
        <h1>My WordPress Site</h1>
        
        <!-- The Angular Element -->
        <my-like-button count="99"></my-like-button>
    
        <!-- The Script -->
        <script src="polyfills.js"></script>
        <script src="main.js"></script>
      </body>
    </html>

🎉 End of Day 24 — What You Learned

Today you liberated your components:

  • ✔️ @angular/elements: The bridge between Angular and the Web.
  • ✔️ createCustomElement(): The conversion magic.
  • ✔️ customElements.define(): Registering the tag with the browser.
  • ✔️ ShadowDOM: Protecting styles from external CSS.

🧪 Day 24 Challenge

Build a “Weather Widget” Element.

Requirements:

  1. Accepts an @Input() city (string).
  2. Fetches mock weather data for that city.
  3. displays temperature and an icon.
  4. Build the project.
  5. Create a blank test.html file on your Desktop.
  6. Include the built JS files and <weather-widget city="London"></weather-widget> in test.html.
  7. Open test.html in Chrome and verify it works!