@hapiness/custom-elements-loader
TypeScript icon, indicating that this package has built-in type declarations

7.2.0 • Public • Published

Hapiness

CUSTOM-ELEMENTS-LOADER

This module exposes a factory to use ElementsLoaderService inside JavaScript's applications like React.js, Vue.js or just standalone.

THIS MODULE IS ONLY USED IN JiT (Just-in-Time) MODE AND THE BUILD WON'T BE THE MOST OPTIMIZED - TO HAVE THE BEST WAY AND THE BEST OPTIMIZED CUSTOM ELEMENTS INTEGRATION WITH AoT (Ahead-of-Time) MODE, CHECK HERE

DON'T USE THIS MODULE FOR ANGULAR APPLICATION

Installation

$ yarn add @hapiness/custom-elements-loader

or

$ npm install --save @hapiness/custom-elements-loader

All required dependencies will be automatically installed : @angular/animations, @angular/common, @angular/core, @angular/compiler, @angular/elements, @angular/platform-browser, @angular/platform-browser-dynamic, @hapiness/ng-elements-loader, @webcomponents/webcomponentsjs, core-js, document-register-element, rxjs and zone.js.

If your custom element module must have more dependencies, you must install them by yourself

Usage

Before to use ElementsLoader exposed by @hapiness/custom-elements-loader, you must create your own custom-elements modules.

To create a new library with Angular-CLI, follow this guide.

1) made-with-love custom element

- Component

This component will be the final custom-element interpreted in your browser.

projects/made-with-love/src/lib/made-with-love.component.ts:

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

@Component({
  selector: 'made-with-love',
  templateUrl: './made-with-love.component.html',
  encapsulation: ViewEncapsulation.ShadowDom
})
export class MadeWithLoveComponent implements OnInit {
  private _name: string;
  private _url: string;
  private _color: string;
  private _size: number;

  constructor() {
    this.size = 1;
    this.color = 'red';
  }

  ngOnInit() {
    if (!this._name || this._name.length === 0) {
      console.error(`Name attribute must be provided!`);
    }
  }
  
  get name(): string {
    return this._name;
  }
  
  @Input()
  set name(n: string) {
    this._name = n;
  }
  
  get url(): string {
    return this._url;
  }

  @Input()
  set url(u: string) {
    this._url = u;
  }
  
  get color(): string {
    return this._color;
  }

  @Input()
  set color(c: string) {
    this._color = c;
  }
  
  get size(): number {
    return this._size;
  }

  @Input()
  set size(s: number) {
    this._size = s;
  }
}

Note: Your component must have encapsulation equals to ViewEncapsulation.ShadowDom if you want to have shadowdomv1 support else you can delete this line to have original support.

projects/made-with-love/src/lib/made-with-love.component.html:

<ng-template #noUrl>
  {{ name }}
</ng-template>
<span [style.font-size.em]="size">
  Made with <span [style.color]="color"></span> by
  <ng-container *ngIf="url && url.length > 0; else noUrl">
    <a [attr.href]="url" target="_blank">{{ name }}</a>
  </ng-container>
</span>

- Module

projects/made-with-love/src/lib/made-with-love.module.ts:

import { NgModule, Type } from '@angular/core';
import { CommonModule } from '@angular/common';
import { WithCustomElementComponent } from '@hapiness/ng-elements-loader';
import { MadeWithLoveComponent } from './made-with-love.component';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [
    MadeWithLoveComponent
  ],
  entryComponents: [
    MadeWithLoveComponent
  ],
  exports: [
    MadeWithLoveComponent
  ]
})
export class MadeWithLoveModule implements WithCustomElementComponent {
  customElementComponent: Type<MadeWithLoveComponent> = MadeWithLoveComponent;
}

Note: Your module must implement WithCustomElementComponent interface exposed by @hapiness/ng-elements-loader and, component must be declared inside entryComponents and declaration meta-data of NgModule.

- Dependencies

The minimum package.json file for your module is described below:

projects/made-with-love/package.json:

{
  "name": "made-with-love",
  "version": "1.0.0",
  "peerDependencies": {
    "@hapiness/custom-elements-loader": "^7.2.0"
  }
}

If your module has to have others dependencies not installed automatically by @hapiness/custom-elements-loader like explained in installation, you must add them in dependencies section.

- Publish your module

Your custom-element module is now ready to be used so you have to publish it before use it in your application.

Back to top

2) made-with-love custom element in your JavaScript application

Create a JavaScript application with your module and @hapiness/custom-elements-loader in dependencies.

Install all dependencies your module must have if not already installed.

- Application contains made-with-love custom element

We create a HTMLfile with our custom element inside.

index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <title>Web Component</title>
</head>
<body>
<div>
    <made-with-love name="Hapiness Framework" url="https://github.com/hapinessjs/" size="2"></made-with-love>
</div>

<script src="./main.js" type="text/javascript"></script>
</body>
</html>

main.js file contains all JavaScript elements to use ElementsLoader and it's built with webpack.

main.ts

// POLYFILLS
import 'zone.js/dist/zone';
import 'core-js/es7/reflect';

/** In browsers that don't support Custom Elements natively **/
// import 'document-register-element';

/** You must add this if your application will be compiled in es5 because the specification requires developers use ES2015 classes to define Custom Elements **/ 
// import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter';

import { ElementsLoader } from '@hapiness/custom-elements-loader';
import { MadeWithLoveModule } from 'made-with-love';

ElementsLoader.loadContainingCustomElements(
    {
        selector: 'made-with-love',
        module: MadeWithLoveModule
    }
).subscribe(undefined, e => console.error(e));

- Explanation

The creation of the custom element happens directly inside HTML file with all attributes we want to display:

<made-with-love name="Hapiness Framework" url="https://github.com/hapinessjs/" size="2"></made-with-love>

Loading of the component happens inside main.ts file.

  • Add required polyfills
import 'zone.js/dist/zone';
import 'core-js/es7/reflect';
  • Additional polyfills can be added if needed:
/** In browsers that don't support Custom Elements natively **/
// import 'document-register-element';

/** You must add this if your application will be compiled in es5 because the specification requires developers use ES2015 classes to define Custom Elements **/ 
// import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter';

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install --save classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';

/**
 * Web Animations `@angular/platform-browser/animations`
 * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
 * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
 **/
// import 'web-animations-js';  // Run `npm install --save web-animations-js`.
  • We call loadContainingCustomElements method of ElementsLoader from @hapiness/custom-elements-loader. This method takes in parameter CustomElementModuleSelector or CustomElementModuleSelector[] from @hapiness/ng-elements-loader.
export interface CustomElementModuleSelector {
    selector: string;
    module: Type<any>;
}

Selector is the custom tag of your custom element and module is the Angular module contains the component.

- Show the result

Launch your application and you will see your custom element displayed inside your JavaScript application:

Made with by Hapiness Framework

Back to top

3) Custom element with custom event

In the previous component we have created only @Input properties but sometimes, you'll want to emit event from your custom element to the DOM with @Ouput properties.

- Custom element

Here an example of a component emits event to its parent:

projects/hello-world/src/lib/hello-world.component.ts:

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

@Component({
  selector: 'hello-world',
  templateUrl: './hello-world.component.html',
  styleUrls: ['./hello-world.component.scss'],
  encapsulation: ViewEncapsulation.ShadowDom
})
export class HelloWorldComponent implements OnInit {
  private _sayHello$: EventEmitter<string>;

  constructor() {
    this._sayHello$ = new EventEmitter<string>();
  }

  ngOnInit() {
  }
  
  @Output('sayHello')
  get sayHello$(): EventEmitter<string> {
    return this._sayHello$;  
  }

  sayHello() {
    this._sayHello$.emit('Hello World');
  }
}

projects/hello-world/src/lib/hello-world.component.html:

<div>
  <button type="button" (click)="sayHello()">Say Hello with Event</button>
</div>

- Use it in your application

To use it and receive event, you must do this:

index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <title>Web Component</title>
</head>
<body>
<div>
    <hello-world></hello-world>
</div>

<script src="./main.js" type="text/javascript"></script>
</body>
</html>

We set a listener to catch sayHello event and do what we want:

main.ts

// POLYFILLS
import 'zone.js/dist/zone';
import 'core-js/es7/reflect';

/** In browsers that don't support Custom Elements natively **/
// import 'document-register-element';

/** You must add this if your application will be compiled in es5 because the specification requires developers use ES2015 classes to define Custom Elements **/ 
// import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter';

import { ElementsLoader } from '@hapiness/custom-elements-loader';
import { HelloWorldModule } from 'hello-world';

ElementsLoader.loadContainingCustomElements(
    {
        selector: 'hello-world',
        module: HelloWorldModule
    }
).subscribe(undefined, e => console.error(e));

document.querySelector('hello-world').addEventListener('sayHello', (event: any) => alert(event.detail));

Back to top

Change History

  • v7.2.0 (2018-11-27)
    • Angular v7.1.0+
    • Add ElementsLoaderService.registerContainingCustomElements() method to be used for AoT compiler
    • ElementsLoaderService.loadContainingCustomElements() method must be used only for JiT compiler
    • Explain how to create an optimized webcomponent bundle with this tutorial
    • Documentation
  • v7.1.0 (2018-11-09)
    • Angular v7.0.3+
    • document-register-elements v1.13.1 latest version of the polyfill only require if your browser doesn't support customElement
    • @webcomponents/webcomponentsjs v2.1.3 to fix issue with es5 compilation outside Angular application like explain here
    • Allow custom elements registration in browser even if tag isn't yet present in the DOM like this, it can be created or loaded asynchronously after registration
    • Documentation
  • v7.0.0 (2018-11-02)
    • Angular v7.0.2+
    • Documentation
  • v6.4.2 (2018-10-18)
    • Angular v6.1.10+
    • Explain how to add new polyfills for reflect api to solve bug reported in this issue
    • Provide ElementsLoaderService in ElementsLoaderModule to solve bug reported in this issue
    • Documentation
  • v6.4.1 (2018-09-26)
    • Fix version to Angular v6.1.7 to avoid the bug reported in this issue
    • Documentation
  • v6.4.0 (2018-07-26)
    • Angular v6.1.0+
    • Documentation

Back to top

Maintainers

tadaweb
Julien Fauville Antoine Gomez Sébastien Ritz Nicolas Jessel

Back to top

License

Copyright (c) 2018 Hapiness Licensed under the MIT license.

Back to top

Package Sidebar

Install

npm i @hapiness/custom-elements-loader

Weekly Downloads

1

Version

7.2.0

License

MIT

Unpacked Size

91.8 kB

Total Files

23

Last publish

Collaborators

  • hapiness