real JS classes in scala.js to develop webcomponents
Asked Answered
L

1

8

I would like to develop some of the webcomponents in a Polymer 2.0 project with scala.js. While there is a wonderful demo-project on github demonstrating how it works with Polymer 1.0. I cannot get something similar to work with Polymer 2.0 and the native Element-registration technique.

A simple facade might look like the following

@ScalaJSDefined
class PolymerElement extends PolymerBase {
  def is: String = ""
  def properties: js.Dynamic = js.Dynamic.literal()
}

@js.native
@JSGlobal("Polymer.Element")
class PolymerBase extends HTMLElement

The actual Element:

@JSExportTopLevel("MyElement")
@ScalaJSDefined
class MyElement extends PolymerElement  {

   private var label = "init"

   override def is = "my-element"

   override def properties = js.Dynamic.literal(
    "label" -> Map(
      "type" -> "String",
      "value" -> "init",
      "notify" -> true
    ).toJSDictionary
  )

  def testMe = {
    println(label)
  }
}

object MyElement {
  @JSExportStatic
  val is: String = MyElement.is

  @JSExportStatic
  val properties: js.Dynamic = MyElement.properties

}

No matter whether I take the old style element registration Polymer(MyElement) or the platform native variant window.customElement.define(MyElement.is, MyElement) It obviously throws an exception as MyElement isn't instatiable with new MyElement. It throws the exception:

Uncaught TypeError: Class constructor PolymerElement cannot be invoked without 'new'

Studying the Scala.js facade writing guide, I already tried a lot of facade variants declaring PolymerElement and PolymerBase abstract.

A possible solution that comes to my mind is, writing a native JavaScript Class, that indeed is instantiable and using @js.native facades on them. But I'm looking for a way to achieve it with something Scala.js 0.6.16 provides.

Lubra answered 15/5, 2017 at 9:28 Comment(4)
Bump into this too. Have you found any solution for this?Wimer
Not really, I discarded the idea, to do it all in scala.js, so that the element skeleton stays normal JS and the relevant helper functions are written in scala.js...I ve been told that ES6 transpilation support will come in v. 1.0 of scala.js soon... Looking forward to thatLubra
@SeDev I see. That was I'm afraid of. Anyway, thanks for help!Wimer
@Lubra Just hit that roadblock today too (stackoverflow.com/questions/47492492) unfortunately. Do you know if there's been any progress towards supporting this in 1.0.0-M1? Or did you find a way to make this work?Precedency
W
2

Updated version (2018-04)

Ok, this is possible helps to someone else too and I decided to publish my new version of it.

I'm using this pure ScalaJS solution to integrate with Polymer2/CustomElements.

My environment is:

  • Scala : 2.12
  • ScalaJS: 0.6.22
  • And also : "org.scala-js" %%% "scalajs-dom" % "0.9.2"

ScalaJS options:

"-P:scalajs:sjsDefinedByDefault"

I've created some ScalaJS facades for CustomElements and Polymer 2 and published them here - https://bitbucket.org/latestbit/scalymer/src/tip/src/main/scala/org/latestbit/sjs/polymer2/?at=default

They're not full-featured Polymer facades, just in the very beginning state, but they are working for me.

And you can use them easily without any hacks like:

@JSExportTopLevel(name = "TestElement")
class TestElement() extends Polymer.Element {

    override def connectedCallback(): Unit = {
        super.connectedCallback()
        global.console.log(s"Attribute name ${getAttribute("name")}. Body is ${dom.document.querySelector("body")}")
        global.console.log(s"${this.$.selectDynamic("testCl")}")
        global.console.log(s"${$$("testCl")}")
    }
}


object TestElement {
    @JSExportStatic
    def is = "test-element"

    @JSExportStatic
    def properties = js.Dictionary(
        "name" -> js.Dictionary(
            "type" -> "String"
        )
    )
}

Then register it also in Scala like:

object TestJsApplication {

    def main() : Unit = {
        Globals.customElements.define(TestElement.is,
            js.constructorOf[TestElement]
        )
    }

}

The html part is usual:

<dom-module id="test-element">

    <template>
        <span id="testCl">Not much here yet.</span>
        This is <b>[[name]]</b>.
    </template>

</dom-module>

You will find the complete example here - https://bitbucket.org/latestbit/scalymer/src


An old try to solve (for historical purposes)

Ok, this is the best 'solution' I've found.

This is not solving it completely, but I hope it'll be a helpful trick while we're expecting sjs improvements in this area.

  1. Get any library to 'mixin' js classes. I've used https://github.com/rse/aggregation.

  2. Create your ScalaJS component but don't try to inherit it from Polymer.Element directly:

@ScalaJSDefined
@JSExportTopLevel("TestPolymerElement")
class TestPolymerElement extends js.Object {
    def test = g.console.log("Hello from scala")
}

object TestPolymerElement {
    @JSExportStatic
    def is = "test-polymer-element"
}
  1. Create a JS pure "companion" class to inherit Polymer.Element and ScalaJS component as a mixin and register it:
class TestPolymerElementJs extends aggregation(Polymer.Element,TestPolymerElement) {
}
customElements.define(TestPolymerElementJs.is, TestPolymerElementJs);

Also, you can define the properties and manage them in ScalaJS like:

@ScalaJSDefined
@JSExportTopLevel("TestPolymerElement")
class TestPolymerElement(val name : String) extends js.Object {

    def test = g.console.log(s"Hello from ${name}")

}

object TestPolymerElement {
    @JSExportStatic
    def is = "test-polymer-element"

    @JSExportStatic
    def properties = js.Dictionary (
        "name" -> js.Dictionary(
            "type" -> "String"
        )
    )
}
Wimer answered 15/7, 2017 at 16:39 Comment(2)
I tried it, and it didn't work properly. Even though it did "compile" and the project ran, I got errors, while trying to access public member val's from the class, only (static) object fields were available, will upload an example project as playground, maybe someone else is able to fix itLubra
@Lubra I've given another try for it - have a look above :)Wimer

© 2022 - 2024 — McMap. All rights reserved.