@twobirds/microcomponents
Version:
Micro Components Organization Class
281 lines (211 loc) • 7.4 kB
Markdown

# Custom Elements
Custom Elements provides two funktions:
- **defineCE()** lets you define a custom tag and add functionality to it. Internally it uses [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define).
- **getCE()** retrieves a custom element constructor from the web component registry via [CustomElementRegistry.get()](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/get).
# Prerequisites
You should at least know Micro Components **[DC](https://gitlab.com/twoBirds/microcomponents/-/blob/main/assets/documentation/MicroComponents.md?ref_type=heads#dc-class-extends-mcbase)** superclass usage, since your own class code extends from it and uses its static functions. It is recommended that you have read and understood the **[Micro Component documentation](https://gitlab.com/twoBirds/microcomponents/-/blob/main/assets/documentation/MicroComponents.md?ref_type=heads)**, since custom elements is just an extension that internally uses micro components.
# Usage
## Create a Custom Element
Before:
```html
<body></body>
```
```javascript
// customElementRegistry.define() wrapper
defineCE(
"my-tag", // tag name
class MyTag extends DC { // definition class
constructor(target, data = {} ) {
super(target);
this.data = data;
target.innerHTML = "Hello World";
}
}
);
let MyTag = getCE("my-tag"),
data = { a: 1 },
myTag = new MyTag( data );
/*
HINT:
At this point the web component is not yet attached to the DOM.
But you can perform any operation on it, e.g. calling methods on its inner micro components.
*/
document.body.append( myTag );
```
After:
```html
<body>
<my-tag _mc>Hello World</my-tag>
</body>
```
**Hints**
- Your code extends the **[DC](https://gitlab.com/twoBirds/microcomponents/-/blob/main/assets/documentation/MicroComponents.md?ref_type=heads#dc-class-extends-mcbase)** superclass. This superclass provides methods to traverse the DOM and select other micro components contained in HTML elements.
- it is essential to understand that your web component code is only one of the numerous micro components that can reside inside `<my-tag>`
- After you created a custom element, the custom element will contain a **property `<my-tag>._mc`** which contains your class instance.
- Also the custom element then has an **attribute '_mc'** which will indicate that at least one micro component is attached to it.
- Note that you can hand over data natively via parameters, when handling custom elements in above way.
- The native way of usage, simply adding the tag to the template, is still possible. You have to use attributes then to hand in data.
## Web Component Lifecycle Events
```javascript
// customElementRegistry.define() wrapper
defineCE(
"my-tag",
class MyTag extends DC {
constructor(target) {
super(target);
target.setAttribute('my-attribute', 'change me');
}
/*
web component specific event handlers
*/
onConnected(ev) {
console.info("MyTag .onConnected() handler", ev);
}
onAttributeChanged(ev) {
console.info("MyTag .onAttributeChanged() handler", ev);
}
onAdopted(ev) {
console.info("MyTag .onAdopted() handler", ev);
}
onDisconnected(ev) {
console.info("MyTag .onDisconnected() handler", ev);
}
},
['my-attribute'] // DOM node attributes to observe
);
let MyTag = getCE("my-tag"), // customElemtentRegistry.get("my-tag")
myTag = new MyTag(); // programmatically create a custom element instance
document.body.append( myTag );
```
## Composition with Micro Components
You can use your Web Component constructor to add other modules to the `<my-tag>` DOM node:
```javascript
class OtherModule extends DC {
constructor(target) {
super(target);
}
onConnected(ev) {
console.info("[OtherModule] .onConnected() handler", ev);
}
}
defineCE(
"my-tag",
class MyTag extends DC {
constructor(target) {
super(target);
DC.add( target, 'otherModule', new OtherModule(target) );
// equivalent:
// target._mc.otherModule = new OtherModule(target);
}
onConnected(ev) {
console.info("[MyTag] .onConnected() handler", ev);
}
}
);
let MyTag = getCE("my-tag");
document.body.append( new MyTag() );
```
**Hints**
- You can add as many other micro components to the DOM node as you like.
- The native web component lifecycle events will be triggered on **all** micro components.
- The event parameter handed over is an internal event instance, that contains the original native event in its `data` property.
## Native HTMLElement Event Handlers
Native event handlers will be auto-attached, when the method name follows one of these two schemes:
- **on[A-Z]...**: e.g. **onClick(){ ... }** will be called when the native event is triggered
- **one[A-Z]...**: e.g. **oneClick(){ ... }** will be called **once** only when the native event is triggered. Subsequent events will be ignored.
```javascript
defineCE(
"my-tag",
class MyTag extends DC {
constructor(target) {
super(target);
target.innerHTML = "Click Me";
}
/*
native event handlers
*/
onClick(ev) {
console.info("MyTag .onClick() handler", ev);
}
}
);
let MyTag = getCE("my-tag");
document.body.append( new MyTag() );
```
**Hints**
- The native web component lifecycle events will be triggered on **all** micro components contained in the custom element, provided the MC contains a matching named handler.
- The event parameter handed over is an internal event instance, that contains the original native event in its `data` property.
# Lifecycle Event Handlers
Currently there is only one lifecycle event handler: the **oneInit(){ ... }** method you provide with your class.
This method is triggered asynchronous via a timeout after you made a new instance of your class.
```javascript
defineCE(
"my-tag",
class MyTag extends DC {
constructor(target) {
super(target);
target.innerHTML = "Hello World";
}
/*
lifecycle event handlers
*/
oneInit(ev) {
console.info("MyTag .onInit() handler", ev);
}
}
);
let MyTag = getCE("my-tag");
document.body.append( new MyTag() );
```
**Hints**
- The native web component lifecycle events will be triggered on **all** micro components contained in the custom element, provided the specific micro component contains a matching named handler.
- The event parameter handed over is an internal event instance that contains no data.
# Custom Events
```javascript
defineCE(
"my-tag",
class MyTag extends DC {
constructor(target) {
super(target);
target.innerHTML = "Hello World";
}
/*
custom event handlers
*/
onSpecialEvent(ev) {
console.log('MyTag .onSpecialEvent() handler', ev);
}
}
);
let MyTag = getCE("my-tag");
document.body.append( new MyTag() );
document
.querySelector( 'my-tag' )
?._mc
.MyTag
.trigger('specialEvent');
/*
Alternatives:
*/
document
.querySelector( 'my-tag' )
?._mc
.trigger('specialEvent'); // ...on all micro components contained in <my-tag>
/*
traversing the DOM:
<my-tag>
<my-inner-tag />
</my-tag>
*/
// <my-tag> method:
onSpecialEvent(ev){
console.log( ev.data );
}
// <my-inner-tag> method:
sendSpecialEvent(){
this.trigger( 'specialEvent', "Hello World", 'u' ); // 'u' = bubble up.
}
```
**Hints**
- The event parameter handed over is an internal event instance, that contains the payload in its `data` property.