2012-08-23 13 views
18

¡En la obra! marco, utilizando Scala, decir que tengo una forma tal como sigue:Jugar! framework 2.0: Validar el campo en formularios usando otros campos

import play.api.data._ 
import play.api.data.Forms._ 
import play.api.data.validation.Constraints._ 

case class User(someStringField: String, someIntField: Int) 

val userForm = Form(
    mapping(
    "someStringField" -> text, 
    "someIntField" -> number verifying(x => SomeMethodThatReceivesAnIntAndReturnsABoolean(x)) 
)(User.apply)(User.unapply) 

)

donde SomeMethodThatReceivesAnIntAndReturnsABoolean es un método que realiza alguna lógica en la int para validarlo.

Sin embargo, me gustaría poder considerar el valor de someStringField al validar someIntField, ¿hay alguna forma de lograr esto en los formularios de frameworks de juego? Yo sé que puedo hacer algo como:

val userForm = Form(
    mapping(
    "someStringField" -> text, 
    "someIntField" -> number 
)(User.apply)(User.unapply) 
.verifying(x => SomeFunctionThatReceivesAnUserAndReturnsABoolean(x)) 

y luego me gustaría tener toda la instancia de usuario disponible pasa a la función de validación. El problema con ese enfoque es que el error resultante se asociaría con el formulario completo en lugar de asociarse con el campo someIntField.

¿Hay alguna manera de obtener ambas cosas, validar un campo usando otro campo y mantener el error asociado al campo específico que deseo validar, en lugar de todo el formulario?

Respuesta

0

Supongo que asignan el código scala a JSR-Validation. Ahí definitivamente no es posible. Hay algunos argumentos para hacer esto. Principalmente que una validación debe ser simple y no hacer una lógica compleja. Como siempre todavía echo de menos esto también. OVal de play1 fue mejor para mí.

+0

Eso es extraño. Pensé que cosas tales como requerir cierto campo cuando otro campo no estaba vacío era algo común, y no se consideraba una lógica compleja. Por lo tanto, es extraño que nadie haya tropezado con esto antes, ¡especialmente con la obra! creadores del marco. –

3

si no te importa tener un prefijo para que params puede agrupar los parametros relacionados:

val aForm = Form(
mapping(
    "prefix" -> tuple(
    "someStringField" -> text, 
    "someIntField" -> number 
) verifying (tup => your verification) 
)(tup => User.apply(tup._1, tup._2)(User.unapply...) 

yo uso algo similar pero sin la asignación de los alrededores. Tendrá que ajustar un poco la aplicación/no aplicación y pasar los argumentos de forma manual para que compile.

El error se registrará en el grupo "prefijo".

También me resulta raro que no se puede registrar errores en cualquier campo desea utilizar formerror la hora de verificar la forma ...

+1

Este enfoque no funciona, porque el error estará asociado al campo "prefijo". Quería que se asociara con "someIntField". Si bien podría aceptar el uso de "prefijo" en su lugar, el problema volverá si necesito que se valide otro campo teniendo en cuenta el valor "someStringField". Ambos errores terminarían asociados al campo "prefijo". –

8

tengo los mismos requisitos con la adición de validación para campos en función del valor de otra campos. No estoy seguro de cómo se hace esto en PLAY 2.2.1 idiomático, pero se me ocurrió la siguiente solución. En este uso estoy degradando el "mapeo" incorporado en un convertidor de tipo simple y aplicando mi validación de "campo intermedio avanzado" en el método "validateForm". El mapeo:

val userForm = Form(
mapping(
    "id" -> optional(longNumber), 
    "surename" -> text, 
    "forename" -> text, 
    "username" -> text, 
    "age" -> number 
)(User.apply)(User.unapply) 
) 

private def validateForm(form:Form[User]) = { 
    if(form("username").value.get == "tom" || form("age").value.get == "38") { 
    form 
     .withError("forename", "tom - forename error") 
     .withError("surename", "tom - surename error") 
    } 
    else 
    form 
} 

def update = Action { implicit request => 
    userForm.bindFromRequest.fold({ 
    formWithErrors => BadRequest(users.edit(validateForm(formWithErrors))) 
    }, { user => 
    val theForm = validateForm(userForm.fill(user)) 
    if(theForm.hasErrors) { 
     BadRequest(users.edit(theForm)) 
    } else { 
     Users.update(user) 
     Redirect(routes.UsersController.index).flashing("notice" -> s"${user.forename} updated!") 
    } 
    }) 
} 

A pesar de que funciona con urgencia Estoy en busca de una versión más idiomática ...

EDIT: Utilice un play.api.data.format.Formatter costumbre en juego idiomático , más en http://workwithplay.com/blog/2013/07/10/advanced-forms-techniques/ - esto le permite agregar errores mediante programación a un formulario.Mi formateador se ve así:

val usernameFormatter = new Formatter[String] { 

override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], String] = { 
    // "data" lets you access all form data values 
    val age = data.get("age").get 
    val username = data.get("username").get 
    if(age == "66") { 
    Left(List(FormError("username", "invalid"), FormError("forename", "invalid"))) 
    } else { 
    Right(username) 
    } 
} 

override def unbind(key: String, value: String): Map[String, String] = { 
    Map(key -> value) 
} 
} 
} 

Inscrita en el mapeo formulario como el siguiente:

mapping(
[...] 
    "username" -> of(usernameFormatter), 
[....] 
+1

Todavía no me siento lo suficientemente idiomático para mí ... Ha pasado mucho tiempo que hice esta pregunta, y desde entonces, he estado trabajando en mi propio marco de validación para scala https://github.com/NovaMage/SValidator . La implementación está casi terminada, pero aún no está bien documentada. Aunque tiene un conjunto completo de pruebas unitarias. Una vez que termine la documentación sobre cómo usarlo y cómo integrarlo con el juego, lo publicaré aquí como respuesta :) –

1

Gracias a Tom Myer, aquí lo que solía

class MatchConstraint[A](val targetField:String, val map:(String, Map[String, String]) => A, val unmap:A => String) extends Formatter[A] { 
    override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], A] = { 
    val first = data.getOrElse(key, "") 
    val second = data.getOrElse(targetField, "") 
    if (first == "" || !first.equals(second)) { 
     Left(List(FormError(key, "Not Match!"))) 
    } 
    else { 
     Right(map(key, data)) 
    } 
    } 

    override def unbind(key: String, value: A): Map[String, String] = Map(key -> unmap(value)) 
} 

Y aquí lo que es mi forma de mirada como

val registerForm = Form(
    mapping(
    "email" -> email.verifying(minLength(6)), 
    "password" -> text(minLength = 6), 
    "passwordConfirmation" -> of(new MatchConstraint[String]("password", (key, data) => data.getOrElse(key, ""), str => str)) 
)(RegisterData.apply)(RegisterData.unapply) 
) 
0

En la documentación: Playframework Documentation

Se puede ver el siguiente código: val userFormConstraintsAdHoc = Form( mapping( "name" -> text, "age" -> number )(UserData.apply)(UserData.unapply) verifying("Failed form constraints!", fields => fields match { case userData => validate(userData.name, userData.age).isDefined }) )

principalmente sólo verificar después de la cancelar la aplicación y que haya asignado todos los campos, para que pueda hacer una validación más completa.

5

Creo que lo que estás buscando es play.api.data.validation.Constraint.

Digamos que tienes una RegisterForm con una lista de predefinido cities y un campo otherCity y que necesita ya sea el cities o otherCity a suministrar, es decir, otherCity debe ser validado si no se proporciona cities:

case class RegisterForm(
    email: String, 
    password: String, 
    cities: Option[List[String]], 
    otherCity: Option[String] 
) 

Se puede escribir una costumbre Constraint torno a esto:

val citiesCheckConstraint: Constraint[RegisterForm] = Constraint("constraints.citiescheck")({ 
    registerForm => 
    // you have access to all the fields in the form here and can 
    // write complex logic here 
    if (registerForm.cities.isDefined || registerForm.otherCity.isDefined) { 
     Valid 
    } else { 
     Invalid(Seq(ValidationError("City must be selected"))) 
    } 
}) 

Y su definición de formulario se convierte en:

val registerForm = Form(
    mapping(
    "email" -> nonEmptyText.verifying(emailCheckConstraint), 
    "password" -> nonEmptyText.verifying(passwordCheckConstraint), 
    "cities" -> optional(list(text)), 
    "other_city" -> optional(text) 
)(RegisterForm.apply)(RegisterForm.unapply).verifying(citiesCheckConstraint) 
) 

En este ejemplo emailCheckConstraintpasswordCheckConstraint y son restricciones personalizadas adicionales que I definidos similar a citiesCheckConstraint. Esto funciona en Play 2.2.x.

ACTUALIZACIÓN: Funciona en Play 2.3.8 también.

+0

¿cómo se establece el error para un campo particular, entonces? –

Cuestiones relacionadas