【问题标题】:How to map an input text into a sequence in Play framework form?如何将输入文本映射到 Play 框架形式的序列中?
【发布时间】:2014-12-04 03:25:15
【问题描述】:

我有一个具有嵌套值的复杂对象,如下所示:

case class Gateway(RouteConfig: RouteConfig, gwType: Boolean, smmpp: Option[ConfigSMPP], modem: Option[ConfigModem]) 

每个对象在 smmpp 或调制解调器成员中都有值。然后在我的嵌套对象中,我有一个列表成员。我根据 Play 文档一步一步写了一个表单映射。

val gatewayForm: Form[SmsGateway] = Form(
    mapping(
      "smsRouteConfig" -> mapping(
        "oracleId" -> optional(longNumber),
        "smsType" -> optional(seq(text)),
      )(RouteConfig.apply)(RouteConfig.unapply),
      "gwType" -> boolean,
      "smmpp" -> optional(mapping(
        "nodeId" -> optional(text),
        "systemType" -> optional(text),
      )(ConfigSMPP.apply)(ConfigSMPP.unapply)),
      "modem" -> optional(mapping(
        "nodeId" -> optional(text),
        "mdType" -> optional(text),
      )(ConfigModem.apply)(ConfigModem.unapply))
    )(SmsGateway.apply)(SmsGateway.unapply)
  )

但现在我有一些问题:

1) 如果调制解调器或 smpp 形式具有值或取决于 gwType 布尔值(0-smpp, 1-modem),则必须显示它们。我不知道该怎么做。

2) 嵌套序列必须以单个字符串格式显示,而不是重复的输入框。例如,如果我在 seq 中有 3 个值的 smsType - 我应该看到一个由空格或逗号分隔的具有 3 个值的文本框,并且在从文本框中提交值后,它们必须再次转换为 seq。 我希望有人能告诉我该怎么做,或者帮助我理解如果我的愿望不真实,我应该怎么做。对不起我的英语不好。

【问题讨论】:

    标签: scala playframework-2.0 mapping


    【解决方案1】:

    Ad1. 由于gwType 不是可选字段,您可以根据该字段的值显示其中一种可选形式。不需要检查调制解调器或 smpp 之一是否有值。 gwType 提供了足够的信息。

    Ad2. html 请求和底层表单对象之间的自定义映射是使用映射方法的最后两个参数完成的。如果映射很简单,我会说一对一的映射,您通常会使用生成的案例类 applyunapply 方法,就像您在示例中使用的那样。由于您必须将基本文本输入映射到一系列字符串,并且这需要手动解析,因此您必须编写自己的 apply 和 unapply 方法。

    由于您的表单模型在下面非常复杂,因此我发布了更简单的示例,以便您更轻松地掌握这个想法。

    让我们把它作为我们的模型对象。

    case class Entity(stringSeq: Seq[String], int: Int)
    

    在视图模板中,我们只想有两个文本输入,如下所示:

    <form action="..." method="post">
        Strings <input name="stringSeq"><br>
        Int <input name="int"><br>
        <button type="submit" value="submit">Send</button>
    </form>
    

    我们需要做的就是用自定义的 apply 和 unapply 方法编写一个表单映射:

    val form = Form[Entity](
      mapping(
        "string" -> nonEmptyText,
        "int" -> number
      )((string, int) => {
        val seq = string.split(",").toSeq
        Entity(seq, int)
      })((form) => {
        val string = form.stringSeq.mkString(",")
        Option((string, form.int))
      })
    
    )
    

    我们编写了一个自定义函数,而不是生成Entity.apply,它将原始请求参数的元组转换为我们的域对象。它负责将提供的字符串拆分为一系列字符串。在替代Entity.unapply 的第二个自定义方法中,我们执行相反的操作,即再次将序列中的项放入单个字符串并返回一个元组,其值取自我们的模型对象。

    这个例子应该为你提供足够的信息来解决你遇到的问题。

    tl;dr

    为了让您的代码更简洁,而不是将这些函数编写为匿名的,您可以显式定义它们。

    object EntityForm {
    
      val form = Form[Entity](
        mapping(
          "string" -> nonEmptyText,
          "int" -> number
        )(formTupleToEntity)(entityToFormTuple)
    
      )
    
      private def formTupleToEntity(string: String, int: Int): Entity = {
        val seq = string.split(",").toSeq
        Entity(seq, int)
      }
    
      private def entityToFormTuple(entity: Entity) = {
        val string = entity.stringSeq.mkString(",")
        Option((string, entity.int))
      }
    
    }
    

    【讨论】:

    • 如果我理解我们只是为我的实体覆盖应用和取消应用功能。我可以在我的伴随对象中执行此操作以获得清晰的代码,还是我必须仅在映射中执行此转换?
    • mapping 方法将两个函数作为参数,因此只要正确传递它们,您就可以在任何地方定义它们。这只是一个简单的例子。如果我是你,我不会在实体伴侣中覆盖 applyunapply,而是让这些方法更接近表单,因为它们的功能与表单密切相关,而不是与实体本身密切相关。
    • 您能否添加一些示例,如何在 apply\unaply 方法上将此转换提取到其他位置,因为我的大脑无法准确理解如何做到这一点?
    • 我已经扩展了我的答案。希望对您有所帮助。
    • 谢谢!也许我很愚蠢,但我无法理解什么是 Feat2Entity 类型,以及如果我们编写该函数 formTupleToEntity 必须返回 Feat2Entity,我们如何返回 Entity 对象。对不起,如果你花了你的时间
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-08
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-17
    相关资源
    最近更新 更多