# Web Components

## Introduction

Web Components are part of an ever-evolving web specification that aims to allow developers to create native self-contained components without the need for additional libraries or transpilation steps. This guide will teach you how to use Aurelia in Web Components.

## Installing The Plugin

To use the plugin, import the interface `IWcElementRegistry` interface from `@aurelia/runtime-html` module and start defining web-component custom elements by calling method `define` on the instance of `IWcElementRegistry`.

WC custom elements can be defined anytime, either at the application start or later. Applications are responsible for ensuring names are unique.

Extending built-in elements is supported via the 3rd parameter of the `define` call, like the `define` call on the global `window.customElements.define` call.

## How it works

* Each of WC custom element will be backed by a view model, like a normal Aurelia element component.
* For each `define` call, a corresponding native custom element class will be created and defined.
* Each bindable property on the backing Aurelia view model will be converted to a reactive attribute (via `observedAttributes`) and reactive property (on the prototype of the extended HTML Element class created).
* Slot: `[au-slot]` is not supported when upgrading an existing element. `slot` can be used as a normal WC custom element.

Notes:

* WC custom element works independently with the Aurelia component. This means the same class can be both a WC custom element and an Aurelia component. Though this should be avoided as it could result in double rendering.
* `containerless` mode is not supported. Use extend-built-in instead if you want to avoid wrappers.
* the defined WC custom elements will continue working even after the owning Aurelia application has stopped.
* `template` info will be retrieved & compiled only once per `define` call. Changing it after this call won't have any effects.
* `bindables` info will be retrieved & compiled only once per `define` call. Changing it after this call won't have any effects.

## Examples

{% hint style="info" %}
For simplicity, all the examples below define elements at the start of an application, but they can be defined at any time.
{% endhint %}

1. Defining a `tick-clock` element

```typescript
import { Aurelia, IWcElementRegistry } from 'aurelia';

Aurelia
  .register(
    AppTask.creating(IWcElementRegistry, registry => {
      registry.define('tick-clock', class TickClock {
        static template = '${message}';

        constructor() {
          this.time = Date.now();
        }

        attaching() {
          this.intervalId = setInterval(() => {
            this.message = `${Date.now() - this.time} seconds passed.`;
          }, 1000)
        }

        detaching() {
          clearInterval(this.intervalId);
        }
      })
    })
  )
  .app(class App {})
  .start();
```

1. Defining a `tick-clock` element using shadow DOM with `open` mode

```typescript
import { Aurelia, IWcElementRegistry } from 'aurelia';

Aurelia
  .register(
    AppTask.creating(IWcElementRegistry, registry => {
      registry.define('tick-clock', class TickClock {
        static template = '${message}';
        static shadowOptions = { mode: 'open' };

        constructor() {
          this.time = Date.now();
        }

        attaching() {
          this.intervalId = setInterval(() => {
            this.message = `${Date.now() - this.time} seconds passed.`;
          }, 1000)
        }

        detaching() {
          clearInterval(this.intervalId);
        }
      })
    })
  )
  .app(class App {})
  .start();
```

1. Injecting the host element into the view model

```typescript
import { INode, Aurelia, IWcElementRegistry } from 'aurelia';

Aurelia
  .register(
    AppTask.creating(IWcElementRegistry, registry => {
      registry.define('tick-clock', class TickClock {
        static template = '${message}';
        static shadowOptions = { mode: 'open' };

        // all these injections result in the same instance
        // listing them all here so that applications can use what they prefer
        // based on HTMLElement 
        static inject = [INode, Element, HTMLElement];

        constructor(node, element, htmlElement) {
          node === element;
          element === htmlElement;
          this.time = Date.now();
        }

        attaching() {
          this.intervalId = setInterval(() => {
            this.message = `${Date.now() - this.time} seconds passed.`;
          }, 1000)
        }

        detaching() {
          clearInterval(this.intervalId);
        }
      })
    })
  )
  .app(class App {})
  .start();
```

1. Defining a `tick-clock` element with `format` bindable property for formatting

```typescript
import { INode, Aurelia, IWcElementRegistry } from 'aurelia';

document.body.innerHTML = '<tick-clock format="short"></tick-clock>';

Aurelia
  .register(
    AppTask.creating(IWcElementRegistry, registry => {
      registry.define('tick-clock', class TickClock {
        static template = '${message}';
        static shadowOptions = { mode: 'open' };
        static bindables = ['format'];

        // all these injections result in the same instance
        // listing them all here so that applications can use what they prefer
        // based on HTMLElement 
        static inject = [INode, Element, HTMLElement];

        constructor(node, element, htmlElement) {
          node === element;
          element === htmlElement;
          this.time = Date.now();
        }

        attaching() {
          this.intervalId = setInterval(() => {
            this.message = `${(Date.now() - this.time)/1000} ${this.format === 'short' ? 's' : 'seconds'} passed.`;
          }, 1000)
        }

        detaching() {
          clearInterval(this.intervalId);
        }
      })
    })
  )
  .app(class App {})
  .start();
```

1. Defining a `tick-clock` element extending built-in `div` element:

```typescript
import { Aurelia, IWcElementRegistry } from 'aurelia';

document.body.innerHTML = '<div is="tick-clock"></div>'

Aurelia
  .register(
    AppTask.creating(IWcElementRegistry, registry => {
      registry.define('tick-clock', class TickClock {
        static template = '${message}';

        constructor() {
          this.time = Date.now();
        }

        attaching() {
          this.intervalId = setInterval(() => {
            this.message = `${Date.now() - this.time} seconds passed.`;
          }, 1000)
        }

        detaching() {
          clearInterval(this.intervalId);
        }
      })
    }, { extends: 'div' })
  )
  .app(class App {})
  .start();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aurelia.io/~/revisions/NShYVVc01DvYc5bgBiwq/developer-guides/web-components.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
