2012-08-23 9 views
18

W grze! ramy za pomocą SCALA powiedzieć, że mają taki kształt, jak następuje:Graj! framework 2.0: Sprawdź poprawność pola w formularzach używając innych pól

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) 

)

gdzie SomeMethodThatReceivesAnIntAndReturnsABoolean jest sposób, który wykonuje pewną logikę int się go walidacji.

Chciałbym jednak móc wziąć pod uwagę wartość someStringField podczas sprawdzania poprawności someIntField, czy istnieje sposób na osiągnięcie tego w formach ram gry? Wiem, że mogę zrobić coś takiego:

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

, a następnie będę mieć całą dostępną instancję użytkownika przekazaną do funkcji sprawdzania poprawności. Problem z tym podejściem polega na tym, że wynikowy błąd byłby związany z całą formą, a nie byłby powiązany z polem someIntField.

Czy istnieje sposób na uzyskanie obu rzeczy, sprawdzanie poprawności pola za pomocą innego pola i zachowanie błędu związanego z określonym polem, które chcę sprawdzić, zamiast całego formularza?

Odpowiedz

0

Zgaduję, że mapują kod scala do JSR-Validation. Tam na pewno nie jest możliwe. Jest kilka argumentów, aby to zrobić. Głównie, że walidacja powinna być prosta i nie tworzyć skomplikowanej logiki. Zawsze jeszcze tęsknię za tym. OVal z play1 był dla mnie lepszy.

+0

To dziwne. Myślałem, że rzeczy takie jak wymaganie pewnego pola, gdy inne pole nie było puste, były czymś powszechnym i nie uznawano za skomplikowaną logikę. To dziwne, nikt wcześniej nie wpadł na to, zwłaszcza na grę! twórcy samych frameworków. –

3

jeśli nie myśli o prefiks Ci params CAN grupę, którą powiązanych params:

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

używam coś podobnego tylko bez otaczającej mapowania. Będziesz musiał nieznacznie dostosować zastosowanie/anulowanie aplikacji i przekazywać argumenty ręcznie, aby mogła się skompilować.

Błąd zostanie zarejestrowany w grupie "prefiks".

Uważam też to dziwne, że nie można zarejestrować błędy na każdym polu, które chcesz przy użyciu Formerror podczas weryfikacji formularza ...

+1

To podejście nie działa, ponieważ błąd zostanie powiązany z polem "prefiks". Chciałem, aby był powiązany z "someIntField". Chociaż mógłbym zaakceptować użycie "przedrostka" zamiast tego, problem powróci, jeśli będę potrzebował innego pola do sprawdzenia, biorąc pod uwagę wartość "someStringField". Oba błędy zostaną w końcu skojarzone z polem "prefiks". –

8

mam te same wymagania, dodając do sprawdzania poprawności pól w zależności od wartości innych pola. Nie jestem pewien, jak to się dzieje w idiomatycznym PLAY 2.2.1, ale wymyśliłem następujące rozwiązanie. W tym użyciu degraduję wbudowane "mapowanie" do prostego konwertera typów i stosuję walidację "zaawansowane pole międzywęzłowe" w metodzie "validateForm". Odwzorowanie:

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!") 
    } 
    }) 
} 

Mimo to działa jestem pilnie szukają bardziej idiomatycznym wersji ...

EDIT: użyć niestandardowego play.api.data.format.Formatter idiomatycznym w sztuce , więcej o http://workwithplay.com/blog/2013/07/10/advanced-forms-techniques/ - pozwala to programowo dodawać błędy do formularza.Moja Formatter wygląda następująco:

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) 
} 
} 
} 

Zarejestrowany w odwzorowaniu formy tak:

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

Nadal nie czuję się wystarczająco idiomatyczna dla mnie ... To było dawno temu zrobiłem to pytanie i od tego czasu pracowałem nad własną strukturą sprawdzania poprawności dla scala https://github.com/NovaMage/SValidator . Implementacja jest prawie ukończona, ale nie jest jeszcze dobrze udokumentowana. Ma jednak pełny zestaw testów jednostkowych. Kiedy skończę dokumentację, jak go używać i jak zintegrować go z grą, wyślę to tutaj jako odpowiedź :) –

1

Dzięki Tom Myer, Oto co kiedyś

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)) 
} 

I tu co moja postać wygląd jak

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

W dokumentacji: Playframework Documentation

można zobaczyć następujący kod: 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 }) )

Głównie po prostu zweryfikować po Wycofywanie i masz wszystkie pola odwzorowany, więc można zrobić pełniejszą weryfikację.

5

Uważam, że to, czego szukasz, to play.api.data.validation.Constraint.

Say masz RegisterForm z listy predefiniowanych cities i pola otherCity i trzeba albo cities lub otherCity mają być dostarczone, tj otherCity powinny być zatwierdzone, jeśli cities nie jest przewidziane:

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

można napisać zwyczaj Constraint wokół to:

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"))) 
    } 
}) 

a twoja postać staje się definicja:

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) 
) 

W tym przykładzie emailCheckConstraint i passwordCheckConstraint dodatkowe ograniczenia na zamówienie, że zdefiniowane podobnie do citiesCheckConstraint. Działa to w Play 2.2.x.

AKTUALIZACJA: Działa również w wersji 2.3.8.

+0

Jak ustawić błąd dla konkretnego pola? –

Powiązane problemy