1

In my Find controller I have a method like:

public Result findLatest(String repoStr) {
    ............
}

Which is linked through a route:

GET     /latest                     controllers.Find.findLatest(repo: String)

Then, I have a form in a view like:

<form action="@routes.Find.findLatest()" method="get">
    ....
    <select name="repo">....</select>
</form>

But obviously that is failing, because it is expecting some parameters that I do not fulfill in the action. What is the correct way to do this without having to end up leaving the findLatest method taking no parameters in my controller?

lqbweb
  • 1,397
  • 1
  • 16
  • 30

4 Answers4

1

You could change the routes to accept an empty string:

GET     /latest/:repo          controllers.Find.findLatest(repo: String = "")

Then configure your controller function to handle empty string.

That way,

<form action="@routes.Find.findLatest()" method="get">
....
<select name="repo">....</select>

will evaluate repo as an empty string at the controller level.

airudah
  • 1,109
  • 13
  • 16
1

Edit: Support for this implementation was dropped in Play v 2.1

You may be interested in Play's Optional parameters e.g. play.libs.F.Option[String]

Example: How to handle optional query parameters in Play framework

GET     /latest/:repo/:artifact     controllers.Find.findLatestArtifact(repo: play.libs.F.Option[String], artifact: play.libs.F.Option[String])

This will allow you flexibility in which arguments need to be provided.

Not sure which language you're using but the link above contains an example for scala and the method declaration in java would look something like:

import play.libs.F.Option;
public static Result findLatestArtifact(Option<String> repo, Option<String> artifact){ ... }

and updated implementation 2.1 Routes with optional parameter - Play 2.1 Scala

EDIT: play 2.1+ Support : Props to @RobertUdah below

Initializing to null:

GET     /latest/               controllers.Find.findLatest(repo: String = null)
GET     /latest/:repo          controllers.Find.findLatest(repo: String)

<form action="@routes.Find.findLatest()" method="get">
Community
  • 1
  • 1
Chrizt0f
  • 304
  • 1
  • 12
  • Thanks, the only small problem is that they have removed the Path binder for Option in Play 2.1. From another post in stackoverflow: Ok, so: "We removed Option[Long] support in path bindables since it doesn't make sense to have a optional path parameter. You can implement your own path bindable that supports it if you please." But passing an Optional instead of some arbitrary value seems to me a lot nicer. Isn't that the reason we have optionals in the first place? Maybe I will rather create 2 different methods in this case, as it would make more sense than passing -1 as id. – lqbweb Oct 02 '15 at 12:07
  • @Ruben Ah you're right. This won't work 2.1+. The other option is to make your named parameters into query parameters e.g. instead of /latest/repo/artifact your url would look similar to /latest?repo=somerepo&artifact=someartifact which is supported 2.1+. Sorry for the confusion. – Chrizt0f Oct 02 '15 at 13:34
  • @RobertUdah's Answer would work better for you if you still want the empty named parameters. If you insist on having the repoStr != null check (vs repoStr != null && !isEmpty()) you can initialize the String as null in routes. e.g. GET /latest/:repo controllers.Find.findLatest(repo: String = null) GET /latest/:repo controllers.Find.findLatest(repo: String) . That would save you from having to put null in your view and would allow for Robert's cleaner
    – Chrizt0f Oct 02 '15 at 14:09
0

Normally all form data go in the body and you can retrieve them in your action method with bindFromRequest() (see docs).

If you really want to pass one form element as a part of the URL then you have to dynamically compose your URL in JavaScript and change your route.

Your route could look like:

GET     /latest/:repo                     controllers.Find.findLatest(repo: String)

And the JavaScript part like (I didn't actually test the code):

<form name="myform" action="javascript:composeUrl();" method="get">
   ....
   <select name="repo">....</select>
</form>

<script>
  function submitform() {
    var formElement = document.getElementsByName("myform");
    var repo = formElement.options[e.selectedIndex].text;
    formElement.action = "/lastest/" + repo;
    formElement.submit();
  }
</script>
Kris
  • 4,053
  • 6
  • 27
  • 41
0

Cavice suggested something close to what I consider the best solution for this (since F.Option are not supported anymore with the default binders in Play 2.1 ).

I ended up leaving the route like:

GET     /latest                     controllers.Find.findLatest(repo=null)

and the view like:

<form action="@routes.Find.findLatest(null)" method="get">
    <select name="repo"> .... </select>
....
</form>

and in the controller:

public Result findLatest(String repoStr) {
    if(repoStr==null) {
        repoStr=Form.form().bindFromRequest().get("repo");
.....

This allows me to have a second route like:

GET     /latest/:repo                     controllers.Find.findLatest(repo: String)
lqbweb
  • 1,397
  • 1
  • 16
  • 30