How do I create “options objects” in Scala.Js?

做~自己de王妃 提交于 2019-11-28 10:04:30
gzm0

We recommend the following (if you chose to create facade-types):

trait AjaxOptions extends js.Object {
  val url: String = js.native
  val success: js.Function0[Unit] = js.native
}

object AjaxOptions {
  def apply(url: String, success: js.Function0[Unit]): AjaxOptions = {
    js.Dynamic.literal(url = url, success = success).asInstanceOf[AjaxOptions]
  }
}

The advantage is that the type-unsafe cast is contained in a single location. Further, if you ever decide to add/remove fields to/from AjaxOptions, you will (hopefully) think of also adapting the companion's apply method. As a result, the typer will inform you where you have to change your invocations (rather than just having the new field set to undefined).

Please refer to What is the suggested way to instantiate a js.Object for API wrappers for more.

Here's a method that I've found to work quite well:

val fooOptions = js.Dynamic.literal().asInstanceOf[FooOptions]
fooOptions.url = ...
fooOptions.bar = ...
jsFunc(..., fooOptions)

Of course this assumes that the FooOptions trait has declared the fields as var. If not, you'll have to use

val fooOptions = js.Dynamic.literal(
   url = ...,
   bar = ...,
)
jsFunc(..., fooOptions)

but that is less type-safe.

If you're declaring your own options trait, you could also add a companion object with an appropriate apply method:

trait FooOptions extends Js.Object {
   var foo: js.String = ???
   var bar: js.String = ???
}

object FooOptions {
   def apply(): FooOptions =
      js.Dynamic.literal().asInstanceOf[FooOptions]
}

That'll make calling code a lot prettier:

val fooOptions = FooOptions()
fooOptions.foo = ...
fooOptions.bar = ...
jsFunc(..., fooOptions)

Since Scala.js has evolved, I'm amending my answer with the current best practice:

At this point, you should use a trait to describe the options object, like this:

trait AjaxOptions extends js.Object {
  var url: String
  var success: UndefOr[js.Function0[Unit]] = js.undefined
}

That UndefOr[T] means "this field might contain T, or might be undefined"; note that you are initializing those to js.undefined, so they have a default value.

Then, at the call site, you simply override the values that you need to set:

val ajaxResult = new AjaxOptions {
  url = "http://www.example.com/foo"
}

Note the curly braces: you're actually creating an anonymous subclass of AjaxOptions here, that does what you want. Any fields you don't override (such as success above) get left as undefined, so the library will use its default.

Old Answer:

This question is pretty old, but since people are still coming here:

If you have a large and complex options object (as is typical of, say, jQuery UI classes), you may want to build a facade for that using JSOptionBuilder, which is found in the jsext library. JSOptionBuilder isn't quite a panacea, but it's a not-too-much boilerplate mechanism for constructing and using arbitrarily complex options objects.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!