Question

I am using Play Framework with Java and with no prior experience with Scala. What I'm trying to achieve is to maintain request parameters (typically GET) across controller and view. More specifically, I need the view to pass the parameters sent to it by the controller (via the query string) back to the controller once it hands over control. A form is generated in the template using the form helper:

@form(routes.Application.authenticate()) 

I know I can access the current request with play.mvc.Controller.request(). I need to append the data submitted by the form to the current query string and pass it all via the URL, or, in case the form method is POST, either append the current query string to the action URL or store the parameters in hidden fields and pass them all through POST.

Is there a straightforward and clean way to ahieve this? At first I tried to pass everything via an object, but then I ran into trouble with the router, plus I couldn't figure out how to pass the data back.

Was it helpful?

Solution 2

With help from this and this I finally figured out how to generate the hidden input fields. Either of the following approaches does the job:

@for((key, value) <- request.queryString) {
  <input type="hidden" name="@key" value="@value" />
}

Or:

@request.queryString.map { case (key,value) =>          
  <input type="hidden" name="@key" value="@value" />
}

In case of POST, @request.queryString can be simply replaced with @request.body.asFormUrlEncoded. Since both methods return Map[String, Seq[String]], one might want to flatten the values (using @value.mkString); however, in my case the code seems to work fine as is. My ignorance about Scala prevents me from delving deeper into what's happening under the hood, but I'm guessing that in each iteration, the first element from the array is returned, which should work as far as HTTP request parameters in my application are concerned. If I ever test this with edge cases, I will update this post.

OTHER TIPS

UPDATE

This is usually can be done via hidden input field. According to this, there are 2 ways of rendering hidden input, but I prefer simplest one:

<input type="hidden" name="hiddenData" value="@data" /> 

On server side, you may get data from HashMap filledForm.data().get("hiddenData"), or via regular databind mechanism. Since your data is undeterministic, you may change name and value on input control or pass HashMap as data. It will be rendered like this: value="{A=B, B=B}" and on server side you will receive HashMap.

To send request via GET you need to use controller accessed via GET at routes file like this:

@helper.form(routes.Application.doLoginGetMethod)

Generic forms

Play framework often lacks documentation for many important features, but at least it have examples in installation folder. %PLAYINSTALLFODLER%\samples\java\forms is what you need.

Documentation is here: Play framework Forms (Scala) Server side(Java), Templates

Anyway, idea is simple - you can use same form API for working with forms on client side and server side.

First create pojo to store data. Attributes needed for validation and visual tuning:

public static class Login
    @Required
    @Email
    public String email;

    @Required
    @MinLength(5)
    public String password;
 }

Second, you need to create your form - it stateless, so can be reused. Pass it to your view:

public static final Form<Login> LOGIN_FORM = form(Login.class);
...
public static Result login() {
    return ok(loginView.render(LOGIN_FORM));
}

On your template use helpers api from views.html.helper to render form and controls. There are plenty of them: checkbox, select, options, textarea and others.

@(loginForm: Form[_])
@import helper._
...
@helper.form(routes.Application.doLogin) { // this differ from original login method

     @if(loginForm.hasGlobalErrors) { // example of validation and form data
         <p class="error">
             @loginForm.globalError.message</span>
         </p>
     }

    @inputText(  // Notice the helper
        loginForm("email"),
        '_showConstraints -> false,
        '_label -> "Your email"
    )

     @inputPassword( // Another helper
         loginForm("password"),
         '_showConstraints -> true,
         '_label -> "Password"
     )
     <input type="submit" value="Login"> // submit button
}

And then on server side receive form:

public static Result doLogin() {
    final Form<Login> filledForm = LOGIN_FORM.bindFromRequest();
// User did not fill everything properly
    if (filledForm.hasErrors()) return badRequest(login.render(filledForm));
return Controller.redirect("Hey!");
}

Of course you will need routes for this:

GET     /login                      controllers.Application.login
POST    /login                      controllers.Application.doLogin
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top