RandomBits
RandomBits

Reputation: 4284

Pass Scala.js class instance to JavaScript that will invoke the function call method

I have a simple Scala.js class (Scala-2.12.10 and Scala-js 0.6.31):

class Foo extends js.Object {
def apply() = { println("apply") }
def bar() = { println("bar") }
}

val myfoo = new Foo()

An instance of Foo will be passed to a JavaScript library which will in turn attempt to invoke the following two methods:

myfoo()
myfoo.bar()

Of course, this will not work because I cannot define the apply method on Foo since it derives from js.Object and that will not translate to calling () on the object.

How does one construct an appropriate class for this scenario?

Update:

Here is some example code based on @sjrd's answer that provides for strong typing while encapsulating the js.Dynamic complexity.

trait FooTrait extends js.Object {
  def own()
  def bar()
}

class Foo extends FooTrait {
  def own() = { println("apply") }
  def bar() = { println("bar") }
}

def makeFoo(foo: FooTrait]) = {
  val jsFoo = ({ () => 
    foo.own()
  }: js.Function0[Unit]).asInstanceOf[js.Dynamic]

  jsFoo.bar = ({ () =>
    foo.bar()
  }: js.Function0[Unit])

  jsFoo
}

val myFoo = makeFoo(new Foo())

Now myFoo is ready to be passed to the JavaScript library.

Upvotes: 1

Views: 116

Answers (1)

sjrd
sjrd

Reputation: 22085

In JavaScript, the only way that a call like myfoo() can be valid is if myfoo was created as a function value. To make myfoo.bar() work in addition, that must be patched on the function value after it's created. There is no way (except proxies, which are another beast entirely) to create myfoo as an object first, from a class or not, and make it answer to myfoo().

Therefore, this is also what you have to do in Scala.js:

val myfoo = ({ () =>
  println("apply")
}: js.Function0[Unit]).asInstanceOf[js.Dynamic]
myfoo.bar = ({ () =>
  println("bar")
}: js.Function0[Unit])

After that, you can pass myfoo to the JavaScript code.

Upvotes: 1

Related Questions