Creating web components with Angular custom elements
Written on by ih
Table of Contents
Skip navigation- Web components - what for?
- What are web components?
- Creating the Angular project
- Changing the project into a web component
- Embedding the web component
Web components - what for?
At the start of my career, I mainly worked with Joomla; during this time, various special requests and our own ideas started to come up, which in turn led to the development of our own custom modules for Joomla. Over the years, however, other CMS such as Contao and Wordpress joined our portfolio, which led to the problem that the code of our Joomla modules could only be partially adopted for modules from other CMS. Not only that: with each port, a new project was added that needed to be maintained and updated. So what now?
This is where web components come in. As they are platform-independent and supported by every major browser; no special treatment for every individual CMS and only one project needing maintenance! As such, converting the module into a web component is an obvious idea.
What are web components?
Web components are a technology that allows developers to create reusable elements, whose code is executed outside of the main DOM. This is achieved using the following three technologies:
Custom Elements
Custom Elements enable developers to define custom HTML tags and their functionalities.
Shadow DOM
Even though web components are well within the DOM, they are effectively rendered within the Shadow DOM. The DOM-tree is appended to the regular DOM and ensures that the web component code is executed separately from the rest of the document code.
HTML-Templates & Slots
HTML-Templates can be used to create reusable markup templates, while the content of these can be defined using slots.
Creating the Angular project
The first obvious step is to create the Angular project. This can be done with the following command:
ng new <Project-Name> --no-standalone
Even though Angular supports web components out of the box, the required libraries will not be added to a new project by default. Instead, they need to be added manually with the following command:
npm i @angular/elements
Now the project is ready to be turned into a web component.
Changing the project into a web component
app.module.ts
The first step is to update the file app.module.ts. The AppModule class will receive the Angular-hook DoBootstrap, and the constructor adds an injector which allows us to define our Custom Element.
The web component will be defined and made available within the Bootstrap-Hook ngDoBootstrap; the createCustomElement-function, true to it's name, creates a custom element using an Angular component (the AppComponent in this case), whereas the customElements.define-function defines the HTML-tag that we wil use to insert our web component within a website. The naming of the HTML-tag is subject to the following rules:
- The first character of the tag must be a lowercase letter (a-z).
- The tag must include at least one hyphen (-).
- The tag name cannot be an existing HTML-tag.
- The tag cannot contain special characters. (=, @, $ etc.)
The website webcomponents.guide provides a tool to check the validity of a tag name, as well as more detailed explanations on the naming conventions.
export class AppModule implements DoBootstrap {
constructor(readonly injector: Injector) {}
ngDoBootstrap() {
const webArticle = createCustomElement(AppComponent, {
injector: this.injector,
});
customElements.define('web-article', webArticle);
}
}
Finally, the following line needs to be removed in order to prevent the Angular error NG05104:
bootstrap: [AppComponent]
Expanding main.ts
After successfully updating app.module.ts, the file main.ts needs to be updated ass follows to prevent the Angular error NG0908, and then we are good to go:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import 'zone.js';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
Building the web component
In order to deploy the web component, it needs to be built first using the following command:
ng build --output-hashing none
Note the flag --output-hashing none
By default, Angular will add a randomly generated hash to the filename of the generated main-file; as that file is imported using it's name, any updates will be easier by skipping the hash using the --outputhasing none flag.
You can also add a build-script in package.json if you don't want to add the flag on every build.
The generated code can be found within the following path.
<Project-folder>/dist/<Project-Name>/browser/main.js
Embedding the web component
The web component can be embedded with the following three steps:
- Upload the generated main.js file.
- Add the main.js file to the site using the script-tag.
- Adding the web component to the site using the tag defined in the app.module.ts file.
Depeding on the system, each of these steps is carried out sligthly differently.
HTML5
Embedding a web component in a simple HTML5-website is pretty easy, as it only requires to add a script-tag the the header, with it's src-attribute pointing to the location of the main.js file. Once this is added, you can embed the web component using the defined custom tag:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Angular Social Feed</title>
<script src="web-article/dist/web-article/browser/main.js"></script>
</head>
<body>
<web-article></web-article>
</body>
</html>
Contao
In order to use the web components with Contao, you'll need to upload the main.js via UI into a public folder. The file can than be added to the head using a page layout (Themes -> Page Layouts) with the option External Javascripts.
After completing these steps, the web component is available on each page that uses the selected layout and it can be inserted using a HTML block element.
When adding the web component, make sure to use the Ungefilterted HTML block, as Contao tends to replace the brackets with HTML-entities, which leads to the tag being displayed as text rather than rendering it.
Joomla
After uploading the main.js via FTP, it need to be added to the head of the template. Joomla-templates usually offer this option in the template settings (System -> Site Template Styles -> Template).
Should your template not have this option, you may also add main.js manually by editing the index.php file of the template (System -> Site Templates -> Template).
Watch out: Use this method as last resort only. It is strongly discouraged to edit core files of a template, as these changes may be overwritten on an update!
After adding the main.js to the head of the template, the web component can than be inserted using a custom module p.Ex.
Wordpress
Using web components on a Wordpress-site is a bit trickier, as it has the be imported on an additional step. First of all, the file need to be uploaded on the following path:
<WP-BASE-FOLDER>/wp-content/themes/<THEME>/<CUSTOM-FOLDER/>
After uploading the file, an import-script needs to be added; you can do this in the Wordpress backend via Tools -> Theme File Editor. Select the right theme where you've uploaded the main.js file using the combo box in the top right corner, then add the following code to the functions.php file:
function add_webcomponent()
{
wp_register_script( 'web-component-script', get_template_directory_uri() . '/web-component/main.js' );
wp_enqueue_script( 'web-component-script' );
}
add_action( 'wp_enqueue_scripts', 'add_webcomponent' );
The PHP function add_webcomponent registers the new script web-component-script, which imports the main.js file into the wordpress site, then executes that script. The function will be executed after its definition. The names add-webcomponent and web-component-script have been freely selected.
After adding the function, the web component can be added within the Wordpress-site using p.Ex. a Custom HTML block.