【问题标题】:Play Validation - Custom form field validation with specific field error播放验证 - 带有特定字段错误的自定义表单字段验证
【发布时间】:2014-04-24 20:02:22
【问题描述】:
case class Address(
  address1: String,
  city: String,
  state: String,
  postal: String,
  country: String
)

Form(
    mapping = mapping(
      "address1" -> nonEmptyText,
      "city" -> nonEmptyText,
      "state" -> nonEmptyText,
      "postal" -> nonEmptyText,
      "country" -> nonEmptyText
    )(Address.apply)(Address.unapply).verifying("Invalid Postal Code!", validatePostal _)
)

def validatePostal(address: Address): Boolean = {
    address.country match {
      case "US" | "CA" =>
        val regex: Regex = ("^(\\d{5}-\\d{4}|\\d{5}|\\d{9})$|^([a-zA-Z]\\d[a-zA-Z]( )?\\d[a-zA-Z]\\d)$").r
        regex.pattern.matcher(address.postal).matches()
      case _ => false
    }
}    

邮政编码的上述表单验证工作正常,表单上显示无效的美国或加拿大邮政编码的全局错误。

我想将错误显示为字段旁边的字段错误,而不是显示在表单顶部的全局错误。

有没有办法使用内置的表单约束或验证方法来实现这一点,而不是 FormError 的?

【问题讨论】:

    标签: validation scala playframework-2.0


    【解决方案1】:

    您可以将约束添加到该字段。然后更新 validatePostal 以接受这两个值的元组。

    Form(
      mapping = mapping(
        "address1" -> nonEmptyText,
        "city" -> nonEmptyText,
        "state" -> nonEmptyText,
        "postal" -> tuple(
          "code" -> nonEmptyText,
          "country" -> nonEmptyText
        ).verifying("Invalid Postal Code!", validatePostal _),
      )((address1, city, state, postal) => Address(address1, city, state, postal._1, postal._2))((address: Address) => Some((address.address1, address.city, address.state, (address.postal, address.country))))
    )
    

    模板:

    @inputText(
      addressForm("postal.code"), 
      '_label -> "Postal code",
      '_help -> "Please enter a valid postal code.",
      '_error -> addressForm.error("postal")
    )
    

    【讨论】:

    • 在他的验证中,他在 validatePostal 定义中同时使用了邮政和国家/地区的值,所以我认为这不是一个有效的解决方案。
    • @Khanser,感谢您指出这一点。我更改了上面的代码来处理这种情况。
    • 如果他不在乎错误是否包含代码和 contry,那是一个完全有效的解决方案 :) 也许可以使用约束来改进 playframework.com/documentation/2.2.x/ScalaCustomValidations 所以验证消息的范围在约束中验证而不是使用它的每个表单。
    • 这仍然将错误附加到邮政字段而不是 postal.code 字段。我在表单字段的正下方显示错误,由于现在没有带有邮政字段的表单输入,因此不会显示错误。这归结为我在问题中提到的相同场景。
    • 您可以在 postal.code 字段上设置 'error 属性。我在上面添加了一个示例。这也使@Khanser 声明有关已包装有关代码和国家/地区的错误的声明无效。请记住,'help 属性不是必需的,只是想向您展示一些附加功能。
    【解决方案2】:

    定义错误,就像您在表单中创建 FormError("","Invalid Postal Code!") 对象一样,因为它没有键(第一个参数),框架不会将错误附加到表单元素。

    在将请求绑定到表单时出现表单错误,您必须创建一个新表单,删除 FormError("","Invalid Postal Code!") 并将其替换为错误 FormError("form.id","message")

    在我们的项目中,我们为 Form 创建了一个隐式定义来替换表单错误(我们找不到创建动态约束验证的方法),这是我们拥有的 2 个定义:

    def replaceError(key: String, newError: FormError): Form[T] = {
      val updatedFormErrors = form.errors.flatMap { fe =>
        if (fe.key == key) {
          if (form.error(newError.key).isDefined) None
          else {
            if (newError.args.isEmpty ) Some(FormError(newError.key,newError.message,fe.args))
            else Some(newError)
          }
        } else {
          Some(fe)
        }
      }
    
      form.copy(errors = updatedFormErrors.foldLeft(Seq[FormError]()) { (z, fe) =>
        if (z.groupBy(_.key).contains(fe.key)) z else z :+ fe
      })
    }
    
    def replaceError(key: String, message: String, newError: FormError): Form[T] = {
      def matchingError(e: FormError) = e.key == key && e.message == message
      val oldError = form.errors.find(matchingError)
      if (oldError.isDefined) {
        val error = if (newError.args.isEmpty) FormError(newError.key,newError.message,oldError.get.args) else newError
        form.copy(errors = form.errors.filterNot(e => e.key == key && e.message == message)).withError(error)
      }
      else form
    }
    

    我们在一个名为 FormCryptBind 的类中有这些(因为我们还用一些加密的东西改进了表单对象),我们像这样定义隐式 def:

    implicit def formBinding[T](form: Form[T])(implicit request: Request[_]) = new FormCryptBind[T](form)
    

    我们这样做是因为只需导入具有此隐式定义的对象,您就可以使用所有 FormCryptBind 定义,因为它们是 Form 的

    我们就这样使用它

    import whatever.FormImprovements._
    ...
    object SomeController extends Controller{
    ...
    def submit = Action{ implicit request =>
    form.bindRequest.fold(
      formWithErrors => {
        val newForm = formWithErrors.replaceError("", "formField.required", FormError("formField", "error.required")
        BadRequest(someView(newForm)
      },
      formDetails => Redirect(anotherView(formDetails))
    }
    

    由于我无法从应用程序中放置实际的实时代码,所以我稍微触摸了一下 :D 所以如果你复制和粘贴会出现编译错误

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多