1

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.

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you -- I was really scratching my head trying to figure this out. I am working on a facade and just to make sure I understand correctly, there is no way to enforce any static typing in this scenario since you have to construct the object piecemeal as a js.Dynamic (which is what the corresponding JavaScript code does more or less)
Of course my actual use case has more requirements and needs to store some data from the bar call to be used by the apply call later. Is that possible in this scenario other than basically using js.Dynamic like JavaScript?
To store data, you can either dynamically store fields on myfoo, with myfoo.someData = "whatever" (in that case you may have to declare myfoo as lazy val with an explicit type: lazy val myfoo: js.Dynamic = ...). Or you can simply declare vars for your data above the definition of myfoo, and use those vars both from apply and bar.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.