Skip to content

Commit

Permalink
Add format methods to FormError (#6414)
Browse files Browse the repository at this point in the history
* Add format methods to FormError

* Update documentation
  • Loading branch information
wsargent authored and gmethvin committed Jan 17, 2017
1 parent 3119067 commit ccff06c
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 15 deletions.
Expand Up @@ -9,7 +9,7 @@ The `play.data` package contains several helpers to handle HTTP form data submis

@[user](code/javaguide/forms/u1/User.java)

To wrap a class you have to inject a `play.data.FormFactory` into your Controller which then allows you to create the form:
To wrap a class you have to inject a [`play.data.FormFactory`](api/java/play/data/FormFactory.html) into your Controller which then allows you to create the form:

@[create](code/javaguide/forms/JavaForms.java)

Expand All @@ -35,7 +35,7 @@ You can also define an ad-hoc validation by adding a `validate` method to your t

@[user](code/javaguide/forms/u3/User.java)

The message returned in the above example will become a global error.
The message returned in the above example will become a global error. Errors are defined as [`play.data.validation.ValidationError`](api/java/play/data/validation/ValidationError.html).

The `validate`-method can return the following types: `String`, `List<ValidationError>` or `Map<String,List<ValidationError>>`

Expand All @@ -57,10 +57,11 @@ Typically, as shown above, the form simply gets passed to a template. Global er

@[global-errors](code/javaguide/forms/view.scala.html)

Errors for a particular field can be rendered in the following manner:
Errors for a particular field can be rendered in the following manner with [`error.format`](api/scala/play/api/data/FormError.html):

@[field-errors](code/javaguide/forms/view.scala.html)

Note that `error.format` takes `messages()` as an argument -- this is an [`play.18n.Messages`](api/java/play/i18n/Messages.html) instance defined in [[JavaI18n]].

## Filling a form with initial default values

Expand Down
Expand Up @@ -83,13 +83,25 @@ public void constraints() {

@Test
public void adhocValidation() {
Form<javaguide.forms.u3.User> userForm = formFactory().form(javaguide.forms.u3.User.class);
Form<javaguide.forms.u3.User> bound = userForm.bind(ImmutableMap.of("email", "e", "password", "p"));
assertThat(bound.hasGlobalErrors(), equalTo(true));
assertThat(bound.globalError().message(), equalTo("Invalid email or password"));
Result result = MockJavaActionHelper.call(new U3UserController(), fakeRequest("POST", "/")
.bodyForm(ImmutableMap.of("email", "e", "password", "p")), mat);

// Run it through the template
assertThat(javaguide.forms.html.view.render(bound).toString(), containsString("Invalid email or password"));
assertThat(contentAsString(result), containsString("Invalid email or password"));
}

public class U3UserController extends MockJavaAction {

public Result index() {
Form<javaguide.forms.u3.User> userForm = formFactory().form(javaguide.forms.u3.User.class).bindFromRequest();

if (userForm.hasErrors()) {
return badRequest(javaguide.forms.html.view.render(userForm));
} else {
javaguide.forms.u3.User user = userForm.get();
return ok("Got user " + user);
}
}
}

public static String authenticate(String email, String password) {
Expand All @@ -98,12 +110,11 @@ public static String authenticate(String email, String password) {

@Test
public void listValidation() {
Form<UserForm> userForm = formFactory().form(UserForm.class).bind(ImmutableMap.of("email", "e"));
assertThat(userForm.errors().get("email"), notNullValue());
assertThat(userForm.errors().get("email").get(0).message(), equalTo("This e-mail is already registered."));
Result result = MockJavaActionHelper.call(new ListValidationController(), fakeRequest("POST", "/")
.bodyForm(ImmutableMap.of("email", "e")), mat);

// Run it through the template
assertThat(javaguide.forms.html.view.render(userForm).toString(), containsString("<p>This e-mail is already registered.</p>"));
assertThat(contentAsString(result), containsString("This e-mail is already registered."));
}

public static class UserForm {
Expand Down Expand Up @@ -134,6 +145,20 @@ public List<ValidationError> validate() {
//#list-validate
}

public class ListValidationController extends MockJavaAction {

public Result index() {
Form<UserForm> userForm = formFactory().form(UserForm.class).bindFromRequest();

if (userForm.hasErrors()) {
return badRequest(javaguide.forms.html.view.render(userForm));
} else {
UserForm user = userForm.get();
return ok("Got user " + user);
}
}
}

@Test
public void handleErrors() {
Result result = MockJavaActionHelper.call(new Controller2(), fakeRequest("POST", "/")
Expand Down
Expand Up @@ -4,14 +4,14 @@
@if(form.hasGlobalErrors) {
<p class="error">
@for(error <- form.globalErrors) {
<p>@Messages(error.messages, error.arguments.toArray: _*)</p>
<p>@error.format(messages())</p>
}
</p>
}
@* #global-errors *@

@* #field-errors *@
@for(error <- form("email").errors) {
<p>@Messages(error.messages, error.arguments.toArray: _*)</p>
<p>@error.format(messages())</p>
}
@* #field-errors *@
Expand Up @@ -209,6 +209,8 @@ Errors attached to a field will render automatically using the form helpers, so

@[form-user-generated](code/scalaguide/forms/scalaforms/views/user.scala.html)

Errors that are not attached to a field can be converted to a string with `error.format`, which takes an implicit [play.api.i18n.Messages](api/scala/play/api/i18n/Messages.html) instance.

Global errors that are not bound to a key do not have a helper and must be defined explicitly in the page:

@[global-errors](code/scalaguide/forms/scalaforms/views/user.scala.html)
Expand Down
Expand Up @@ -29,7 +29,7 @@
@if(userForm.hasGlobalErrors) {
<ul>
@for(error <- userForm.globalErrors) {
<li>@Messages(error.messages, error.args)</li>
<li>@error.format</li>
}
</ul>
}
Expand Down
Expand Up @@ -6,6 +6,7 @@
import java.util.*;

import com.google.common.collect.ImmutableList;
import play.i18n.Messages;

/**
* A form validation error.
Expand Down Expand Up @@ -88,6 +89,16 @@ public List<Object> arguments() {
return arguments;
}

/**
* Returns the formatted error message (message + arguments) in the given Messages.
*
* @param messagesObj the play.i18n.Messages object containing the language.
* @return the results of messagesObj.at(messages, arguments).
*/
public String format(Messages messagesObj) {
return messagesObj.at(messages, arguments);
}

public String toString() {
return "ValidationError(" + key + "," + messages + "," + arguments + ")";
}
Expand Down
7 changes: 7 additions & 0 deletions framework/src/play/src/main/scala/play/api/data/Form.scala
Expand Up @@ -420,6 +420,13 @@ case class FormError(key: String, messages: Seq[String], args: Seq[Any] = Nil) {
* @param message The new message.
*/
def withMessage(message: String): FormError = FormError(key, message)

/**
* Displays the formatted message, for use in a template.
*/
def format(implicit messages: play.api.i18n.Messages): String = {
messages.apply(message, args)
}
}

object FormError {
Expand Down

0 comments on commit ccff06c

Please sign in to comment.