Angular Elements: Build Once, Use Everywhere (Plus Firebase Realtime!)



Introduction

Angular Elements are a game-changer, allowing you to build Angular components and use them outside of a traditional Angular application. This post will guide you through the process of creating a simple Angular component, converting it into a custom element, integrating it with Firebase for real-time data, and ultimately exporting it for use in any web application, be it WordPress, Ruby on Rails, React, or Vue.


1. Setting Up Your Angular Project and Installing Dependencies

First, you'll need to generate a new Angular application using the Angular CLI. Make sure you have Angular version 6 or later. Once your project is set up, install Angular Elements and the necessary polyfills for browser compatibility. These polyfills can be found in the Web Components Monorepo. Add the import for the custom elements polyfill to your polyfills.ts file.

Next, install Firebase and AngularFire2, which we'll use for our real-time database. Finally, generate a new component, for example, named user-poll, using the Angular CLI.


2. Converting Your Component to a Custom Element

To transform your Angular component into a custom element, navigate to your app.module.ts file. Import the necessary dependencies, including AngularFireModule (if you're using Firebase), Injector from @angular/core, and createCustomElement from @angular/elements. Ensure that your component (e.g., UserPollComponent) is included in the declarations array.

Because this component is not declared in the HTML and not used by the router you need to add it to the entryComponents array.

Within the AppModule class, inject the Injector in the constructor. Then, implement the ngDoBootstrap method. This method is necessary because we need to manually bootstrap the application. Use the createCustomElement method to convert your Angular component into a custom element compatible with native DOM APIs. Register the custom element using customElements.define, providing a name for the element (e.g., user-poll) and the element you created.

An example of how to convert your component in `app.module.ts`:


import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { UserPollComponent } from './user-poll/user-poll.component';

@NgModule({
  declarations: [
    UserPollComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  entryComponents: [UserPollComponent],
})
export class AppModule {
  constructor(private injector: Injector) { }

  ngDoBootstrap() {
    const userPollElement = createCustomElement(UserPollComponent, { injector: this.injector });
    customElements.define('user-poll', userPollElement);
  }
}
        

3. Adding Functionality with Firebase and Styling with Shadow DOM

Within your component, you can implement features such as data retrieval and event handling. For instance, you can use AngularFire2 to connect to a Firestore database and record user votes. Remember to adjust your Firestore settings if you encounter timestamp errors.

To ensure proper styling encapsulation, use ViewEncapsulation.ShadowDom in your component. This compiles all CSS styles into JavaScript, allowing you to use the component's styles without needing to import separate CSS files. This is crucial for using the component outside of Angular.

Here's a simple snippet of how to incorporate the Shadow DOM:


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

@Component({
  selector: 'app-user-poll',
  templateUrl: './user-poll.component.html',
  styleUrls: ['./user-poll.component.css'],
  encapsulation: ViewEncapsulation.ShadowDom
})
export class UserPollComponent {
  // Component logic here
}
        

4. Exporting Your Component for External Use

The goal is to combine all the production build files into a single script that can be included in any web application. The Angular CLI may offer a single command for this in the future, but here's a way to do it manually.

First, install the fs-extra and concat npm packages. Create a build-script.js file in the root of your project. This script will grab the three JavaScript files that Angular builds for production (inline, polyfills, and main) and concatenate them into a single file. Make sure to concatenate the files in that specific order.

Create a directory called elements in your project and save the final output as userpoll.js in that directory. Set up an npm script in your package.json to run the Angular production build with output hashing set to false, followed by the build-script.js script using Node.js. This will create the elements directory with a single JavaScript file that can be used in any external web app.

An example of the build script logic:


const fs = require('fs-extra');
const concat = require('concat');

(async function build() {
  const files = [
    './dist/{your-project-name}/runtime.js',
    './dist/{your-project-name}/polyfills.js',
    './dist/{your-project-name}/main.js'
  ];

  await fs.ensureDir('elements');
  await concat(files, 'elements/user-poll.js');
  console.log('Elements created successfully!');
})();
        

In your HTML file where you want to use this component, you can now include the userpoll.js script tag after putting the <user-poll></user-poll> element in the HTML body.


Conclusion

Angular Elements provide a powerful way to create reusable components that can be used in any web application. By converting Angular components into custom elements, you can leverage the power of Angular's features like data binding and dependency injection in a framework-agnostic manner. The key takeaways are understanding how to convert components, handle styling with Shadow DOM, and concatenate the necessary files for external use.

Keywords: Angular Elements, Custom Elements, Web Components, Firebase, AngularFire2

Post a Comment

0 Comments