简体   繁体   中英

Calling Scala.js code from a native Javascript handler

I have a Scala.js application using Udash . The application is using some Bootstrap extensions, which directly manipulate HTML DOM. I would like to traverse this DOM and add some more handlers to it (eventually I would like the handlers to implement Udash binding ). My trouble is the only way I can do this is by inserting a script tag, which expects me to provide a plain Javascript code.

Is there some way I could call by Scala.js code from this Javascript? Normally I would export a global function and pass any necessary parameters to it, however I do not see any clean way how to pass this , the only way I can think of is using a global variable, which look super ugly to me. Is there anything like local exports, or some other way how to create a JavaScript code I could pass into script which would be able to access Scala.js constructs?

My current code looks like this:

// somewhere in class ExtTable .. in the `render` method
    div(
      p(id := componentId, "Html constructed here"),
      script {
        ExtTable.callback = { e =>
          println(s"JS Callback for $e on $this")
        }
        //language=JavaScript
        s"""
        // I would like to implement this in Scala.js instead
        var t = $$('#${componentId.toString}');
        t.bootstrapTable();
        t.find("tr td:first-of-type").each(function(i,e){
          ExtTable.callback(e);
        })
        """
      }
    ).render
@js.annotation.JSExportTopLevel("ExtTable")
object ExtTable {
  @js.annotation.JSExport
  var callback: js.Function1[Element, Unit] = _
}

TRANSLATE YOUR SNIPPED TO SCALAJS

You need to use some jquery wrapper library. Udash has its own... I'm using oldest one ( here ) (example below).

import org.scalajs.jquery.jQuery
import org.scalajs.dom.Element
import scala.scalajs.js

@js.annotation.JSExportTopLevel("ExtTable")
object ExtTable {
  @js.annotation.JSExport
  var callback: js.ThisFunction0[Element, Unit] = _ //look first argument is what `this` in js means
}

var t = jQuery("#"+componentId.toString);
t.asInstanceOf[js.Dynamic].bootstrapTable(); //to call function that is not known statically!
t.find("tr td:first-of-type").each( { (e: Element) =>
  ExtTable.callback(e)
})
  • To cover funtion with this in body you need to use js.ThisFunction (find more here ). In above example .each(...) takes ThisFunction1[Element,_] and it means this from javascript will be our first argument ( e:Element in code above). As you can see it is inferred from normal scala closure notation ( {(e: Element) => ...} ).
  • weird .asInstanceOf[js.Dynamic] part is necessary here to call function on jquery object that comes from jquery addon. js.Dynamic is special type that you can call any method on it, and compiler will just translate it to same call on js site ( doc ). You can understand it as "Trust me... there will be such method on runtime". Creator of jquery facade cannot assume what addons you would use, and you need to create your own facades or use it dynamically as shown above.

CALLING FUNCTION ON ELEMENT CREATED BY SCALATAGS

You can also create Modifier that will call your code when object is created. Be awared that it will be called before element is injected into dom :

import scalatags.JsDom.all._
import org.scalajs.jquery.jQuery
import org.scalajs.dom.Element
import scala.scalajs.js
import scalatags.JsDom.Modifier

val bootstrapTable:Modifier = new Modifier {
    override def applyTo(t0: Element): Unit = {
      val t = jQuery(t0)
      t.asInstanceOf[js.Dynamic].bootstrapTable()
      t.find("tr td:first-of-type").each(i => {
        ExtTable.callback(e)
      })
  }
//in scala 2.12 you can write it simpler
val bootstrapTable2:Modifier = (t0: Element) => {
      val t = jQuery(t0)
      ...
  }

div(p(
  id := "componentId", 
  "Html constructed here",
  //use modifier here,
  bootstrapTable,
  //you can create new anonymous modifier here: 
  new scalatags.JsDom.Modifier {
    override def applyTo(t: Element): Unit = println("ex1:" + t.outerHTML)
  }
  //in scala 2.12+ you can do it like that (same semantic as above example)
  (e:Element) => println("ex2:" +e.outerHTML)
)).render
  • Here you don't need to use jQuery("#" + componentId) because you have access to Element ( jQuery(t0) ).
  • scala SAM 2.12 makes it even simpler as you can see. You can create instance of Modifier using closure notation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM