UNPKG

@twobirds/microcomponents

Version:

Micro Components Organization Class

281 lines (211 loc) 7.4 kB
![logo](./MicroComponents.svg) # 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.