如何在创建Facade时在scala-js中正确键入javascript回调函数

时间:2015-07-27 20:23:06

标签: javascript scala scala.js

对于许多具有异步操作的javascript库,您可以传递回调函数。我已经阅读了此SO questionthis one tooread the docs,但在创建外观时如何在scala-js中正确键入回调函数仍然有点困惑。我正在为Cloudinary's upload widget编写一个外观,它有一个openUploadWidget方法,可以从他们的文档中获取选项和回调,如下例:

cloudinary.openUploadWidget(
  { cloud_name: 'demo', upload_preset: 'a5vxnzbp'}, 
  function(error, result) { console.log(error, result) });

这是我到目前为止在我的scala-js门面实现的:

object Cloudinary {
  def openUploadWidget(
      options: WidgetOptions,
      callback: (Either[String, Seq[UploadResult]]) => Unit): Unit = {
    _Cloudinary.openUploadWidget(
        options, 
        (error: String, results: js.Array[js.Dynamic]) => {
            callback(Option(results)
                .filterNot(_.isEmpty)
                .map(_.toSeq.map(_.asInstanceOf[UploadResult]))
                .toRight(error))
        })
  }    
}    

@JSName("cloudinary")
object _Cloudinary extends js.Object {
  def openUploadWidget(
      options: WidgetOptions,
      callback: js.Function2[String, js.Array[js.Dynamic], _]): Unit = js.native
}

trait WidgetOptions extends js.Object {
  @JSName("cloud_name") val cloudName: String = js.native
  @JSName("upload_preset") val uploadPreset: String = js.native
}

object WidgetOptions {
  def apply(cloudName: String, uploadPreset: String): WidgetOptions = {
    js.Dynamic.literal(
      cloud_name = cloudName, 
      upload_preset = uploadPreset).asInstanceOf[WidgetOptions]
}

trait UploadResult extends js.Object {
  @JSName("public_id") val publicId: String = js.native
  @JSName("secure_url") val secureUrl: String = js.native
}

你会像以下一样使用它:

def callback(results: Either[String, Seq[UploadResult]]): Unit = {}

def show(): Unit = {
  Cloudinary.openUploadWidget(
      WidgetOptions(
          cloudName = "demo",
          uploadPreset = "a5vxnzbp"),
      callback _)
}

我实现了一个小包装器,从javascript回调args转换为更多Scala-ish,因为我无法弄清楚如何以更直接的方式键入回调。这不是坏事,恕我直言,但我有一种潜在的怀疑,即我不理解某些事情,而且可以做得更好。

任何帮助/建议?

1 个答案:

答案 0 :(得分:1)

感谢Per Wiklander提醒您跟进此事。以下代码是我在实施建议并升级到Scala.js 0.6.6

后确定的
import scala.scalajs.js
import scala.scalajs.js.annotation.JSName

object Cloudinary {
  type CloudinaryCallback = (Either[String, Seq[UploadResult]]) => Unit

  def openUploadWidget(
      options: WidgetOptions,
      callback: CloudinaryCallback): Unit = {
    _Cloudinary.openUploadWidget(options, (error: js.Dynamic, results: js.UndefOr[js.Array[UploadResult]]) => {
      callback(results
          .filterNot(_.isEmpty)
          .map(_.toSeq)
          .toRight(error.toString))
    })
  }
}

@js.native
@JSName("cloudinary")
object _Cloudinary extends js.Object {
  def openUploadWidget(
      options: WidgetOptions,
      callback: js.Function2[js.Dynamic, js.UndefOr[js.Array[UploadResult]], _]): Unit = js.native
}

@js.native
trait UploadResult extends js.Object {
  @JSName("public_id") val publicId: String = js.native
  @JSName("secure_url") val secureUrl: String = js.native
  @JSName("thumbnail_url") val thumbnailUrl: String = js.native
  @JSName("resource_name") val resourceName: String = js.native

  val `type`: String = js.native
  val path: String = js.native
  val url: String = js.native
  val version: Long = js.native
  val width: Int = js.native
  val signature: String = js.native
}

@js.native
trait WidgetOptions extends js.Object {
  @JSName("cloud_name") val cloudName: String = js.native
  @JSName("upload_preset") val uploadPreset: String = js.native
  @JSName("show_powered_by") val showPoweredBy: Boolean = js.native
  @JSName("cropping_default_selection_ratio") val croppingDefaultSelectionRatio: Double = js.native

  val sources: Array[String] = js.native
  val multiple: Boolean = js.native
  val cropping: String = js.native
  val theme: String = js.native
  val text: Map[String, String] = js.native
}

object WidgetOptions {
  def apply(cloudName: String, uploadPreset: String): WidgetOptions = {
    val map: Map[String, js.Any] = Map(
        "sources.local.title" -> "Local Files",
        "sources.local.drop_file" -> "Drop credit card image here",
        "sources.local.select_file" -> "Select File")

    js.Dynamic.literal(
        cloud_name = cloudName,
        upload_preset = uploadPreset,
        sources = js.Array("local"),
        multiple = false,
        cropping = "server",
        theme = "minimal",
        show_powered_by = false,
        cropping_default_selection_ratio = 1.0d,
        text = js.Dynamic.literal.applyDynamic("apply")(map.toSeq: _*)).asInstanceOf[WidgetOptions]
  }
}