Creating a custom element / webcomponent


#1

Hi,

Has anyone figured out how to create a custom element?

An example of this is documented here:

Google developer docs

and further documented here:

Mozilla docs

The principle is to call window.customElements.define(elementName, <class literal>)

In KotlinJS’ dom bindings, customElements.define is declared as

fun define(name: String, constructor: () -> dynamic, options: ElementDefinitionOptions = definedExternally): Unit

The following causes a compilation error:

    abstract class MyComponent : Element() {
      companion object {
        fun init() {
          console.log("registering")
          window.customElements.define("my-component", ::MyComponent )
          console.log("registered")
        }
      }
      init {
        this.textContent = "my-component"
      }
    }

Compilation fails at the line with ::MyComponent because MyComponent is abstract. It has to be abstract because it cannot extend from Element (and external abstract class!) without implementing all members.

Any thoughts on how this problem could be resolved please?

thanks
Fuzz.


#2

Just as a thought - is the use of abstract redundant and perhaps not helpful, when declaring with external?


#3
window.customElements.define("my-component", ::MyComponent )

The proper usage would be something like

window.customElements.define(
    "my-component",
    MyComponent::class.js.unsafeCast<() -> dynamic>()
)

For convenience, you can declare a function like this:

fun CustomElementRegistry.define(
    name: String, 
    constructor: KClass<in Element>
) {
    define(name, constructor.js.unsafeCast<() -> dynamic>())
}

#4

Thanks Alexey, will try that and report back.

Being able to create first class web components as Kotlin classes is very compelling :slight_smile:


#5

Did you manage to create custom elements with KotlinJS ? I tried the code given by Alexey but unfortunately the generated code throws an error :

Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
at new CounterElement (komponent_main.js:161)

Here is my code :

abstract class CustomElement : HTMLElement() {
	init {
		println("Creating custom element")
		textContent = "Hello world"
	}
}

fun <T : Element> defineElement(name: String, constructor: KClass<T>) {
	window.customElements.define(name, constructor.js.unsafeCast<() -> dynamic>())
}

fun main(args: Array<String>) {
	defineElement("custom-element", CustomElement::class)
}

#6

I tried to create a custom element with hand-written ES5 code and it does not work:

function CustomElement() {
    HTMLElement.call(this);
    console.log("Custom element created")
}
CustomElement.prototype = Object.create(HTMLElement.prototype);
CustomElement.prototype.constructor = HTMLElement;

window.customElements.define("custom-element", CustomElement);

However, with ES6 everything works as expected:

class CustomElement extends HTMLElement {
    constructor() {
        super();
        console.log("Custom element created");
    }
}

window.customElements.define("custom-element", CustomElement);

It seems that web components don’t support ES6 classes, at least according to this discussion. And Kotlin/JS does not support ES6 target. We plan to support ES6 eventually, but currently we don’t have any current plans for that.


#7

I actually managed to make it work :slight_smile: This is indeed because KotlinJS compiles to ES5, so we have to add an additional polyfill custom-elements-es5-adapterjs as described here : https://github.com/webcomponents/webcomponentsjs#user-content-custom-elements-es5-adapterjs

I started to write a library to write custom elements with KotlinJS (as well as KotlinJS wrappers for Polymer elements) and I think that it looks quite promising so I can keep you up-to-date on that if you’d like :slight_smile:


#8

Has anyone of you a working example online that would help me getting started?


#9

I have a small working demo of a small POC I made a few months ago if you are still interested :

$ git clone https://github.com/jdemeulenaere/komponent.git
$ ./gradlew gulp_default
$ ./gradlew build -t

Fire up a local server and open index.html :slight_smile:


#10

Hi i want this polymer lib


#11

Thanks for getting back tp me. Took a while for me to notice. I’ll give it a try today!