Custom Web Component

ES6 JS Native Custom Web Component Sugar

<abc-xyz foo="bar">123</abc-xyz>

<p is="word-count">Hello</p>

An ES6 Javascript custom web component wrapper, for generating re-usable native custom HTML based web components. Use as an ES6 module or bundle for old browsers; supported by IE11 and all modern evergreen web browsers.

Stable Version @1.2.6

Follow the project here, or the code on github. Feel free to share the love if you think we're onto something, or just share your usage.

VIEW HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Boom!</title>

    <!-- Polyfill -->
    <script src="/node_modules/promise-polyfill/dist/polyfill.min.js"></script>
    <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>

    <!-- Bootstrap App -->
    <script type="module" src="./index.mjs"></script>
    <script nomodule src="./index.js"></script>
  </head>

  <body>
    <h1>Example View</h1>
    <p>Show a component in action below...</p>

    <hello-world bar="bar!">
      <p slot="main">Hello</p>
      <p slot="footer">World</p>
    </hello-world>

    <p>We have pre built components, npm install razilocomponents (with an 's';)</p>
  </body>
</html>
COMPONENT ES6 JS

import { CustomHTMLElement, html } from "../node_modules/custom-web-component/index.js";

/**
 * HelloWorld
 * A sample Custom HTML Element, to be used in any system that is capable of outputting HTML
 * Build on Web Standards and polyfilled for legacy browsers, using a simple clean lite HTML template rendering called lit-html
 */
class HelloWorld extends CustomHTMLElement {

  /**
   * @public constructor()
   * Invoked when instantiation of class happens
   * NOTE: Call super() first!
   * NOTE: Declare local properties here... [this.__private, this._protected, this.public]
   * NOTE: Declarations and kick starts only... no business logic here!
   */
  constructor() {
    super();

    this.foo = 'FOO!!';
    this.bar;
  }

  /**
   * template()
   * Return html TemplateResolver a list of observed properties, that will call propertyChanged() when mutated
   * @return {TemplateResult} Returns a HTML TemplateResult to be used for the basis of the elements DOM structure
   */
  static template() {
    return html`
      <style>
        /* Style auto encapsulates in shadowDOM or shims for IE */
        :host { display: block; }
        
		div {
			display: block;
			padding: 20px;
			color: #222;
			background: #f5f2f0;
			border: 1px solid #ccc;
			border-radius: 3px;
        }

        button {
            border: none;
            background: #444;
            color: white;
            padding: 10px;
        }
      </style>

      <div>
        <p>
          <slot name="main">Default text if no slot for main</slot>
          <br />
          <strong>FOO:</strong> ${this.foo}
          <br />
          <strong>BAR:</strong> ${this.bar}
          <br />
          <slot name="footer">Default text if no slot for footer</slot>
          <button @click="${this._clicked.bind(this, 'something')}">Boo</button>
        </p>
      </div>
    `;
  }

  /**
   * @static @get observedProperties()
   * Return a list of observed properties, that will call propertyChanged() when mutated
   * @return {Array} List of properties that will promote the callback to be called on mutation
   */
  static get observedProperties() { return ['foo', 'bar']; }

  /**
   * @public propertyChanged()
   * Invoked when an observed instantiated property has changed
   * @param {String} property The name of the property that changed
   * @param {*} oldValue The old value before hte change
   * @param {*} newValue The new value after the change
   */
  propertyChanged(property, oldValue, newValue) {
    console.log(this.tagName, 'propertyChanged', property, oldValue, newValue);

    this.updateTemplate();
  }

  /**
   * @static @get observedAttributes()
   * Return a list of observed attributes, that will call attributeChanged() when mutated
   * @return {Array} List of attributes that will promote the callback to be called on mutation
   */
  static get observedAttributes() { return ['bar']; }

  /**
   * @public attributeChanged()
   * Invoked when an observed node attribute has changed
   * @param {String} attribute The name of the attribute that changed
   * @param {*} oldValue The old value before hte change
   * @param {*} newValue The new value after the change
   */
  attributeChanged(attribute, oldValue, newValue) {
    console.log(this.tagName, 'attributeChanged', attribute, oldValue, newValue);

    if (attribute === 'bar') this.bar = newValue;

    this.updateTemplate();
  }

  /**
   * @public connected()
   * Invoked when node is connected/added to the DOM
   */
  connected() {
    console.log('connected');
  }

  /**
   * @public disconnected()
   * Invoked when node is disconnected/removed from the DOM
   */
  disconnected() {
    console.log('disconnected');
  }

  /**
   * @public update() [parent class]
   * Update the view, pushing only changes for update in shadow DOM
   */
  templateUpdated() {
    console.log(this.shadowRoot, this.tagName, 'updated');
  }

  _clicked(text, ev) {
    alert(text);
  }
}

customElements.define('hello-world', HelloWorld);

Hello

World

What is It?

Custom Web Component is an ES6 JS custom web component helper library for creating dynamic web components to consume in your web applications, be they angular, react, vue or native HTML pages. It will allow you to take the mystery away from native web components and bring them down to earth, let you see them simply, cleanly and cut through the chaff. Import it into an ES6 app and use that or add to an ES6 module js include, just use components or make an app component in another framework and shove stuff in that, seriously its a html element!

What does It Do?

Well, in short it takes the process of adding a custom web component, and simpliefies it, whilst added in missing hooks and supporting more browsers at the same time. It also offers templating/binding (lit-html) from component model to template in isolation, before applying to the dom, letting you do templates, style and bind them with logic. Use them by importing them through ES6 imports using JS modular importing, falling back to bundles for naughty browsers. Create components once, use them in ANY app, framework or static HTML.

Whats Supported?

Well you can use any proper evergreen modern browser, evergreen meaning a browser that updates itself routinely to keep it's self, well, up to date. We can do this through the use of a polyfill to patch up missing browser features, on any browser that does not have features natively. Once browsers adopt the new standards, polyfills stop running; as browsers change, your code gets better with native support!

How Large is it?

The size is not straight forward to work out, all JS files account for just over 5kb, thats peanuts by today's standards, not accounting for lit-html. As the wrapper is a modular library, you only pull in what you need on supported browsers, leveraging caching/dependences at the browser

5.7kb Combined!

Github

The libary can be downloaded via Github, I would recommend using npm though; it's really very simple and takes care of dependencies for you. Here is the main project on github. Installing manually from a download will mean you will have to look recursively through all package files to ensure you get all dependencies; if you use npm to install, all dependences will be installed.

Node and npm

Install the tool and all dependencies simply with a single command via your command line terminal of choice. Use the flag to save to your applications package file.

npm install custom-web-component --save