Reputation: 2441
By default, when one has a route to an action that takes a list parameter
GET /list controllers.Application.index(ids: List[Long])
Play expects that the request uri looks like
/list?ids=1&ids=2&ids=3
But Jquery.ajax by default sends request as
/list?ids%5B%5D=1&ids%5B5D=2&ids%5B%5D=3
Play has something called QueryStringBindable that may be used to change this behavior. Isn't it ? If it can be used to solve this issue, how to implements may custom querybinders ? Or is there another solutions ?
Upvotes: 0
Views: 125
Reputation: 8202
You would be better using the JavaScript routing support in Play.
In your controller, add the following method
def jsRoutes = Action { implicit request =>
Ok(Routes.javascriptRouter("jsRoutes")(
controllers.routes.javascript.Application.index))
.as("text/javascript")
}
Expose it in your routes file, above the /assets/*file entry
GET /assets/js/routes controllers.Application.jsRoutes()
GET /assets/*file controllers.Assets.at(path="/public", file)
Add a tag to your view to get the JavaScript it generates
<script src="@controllers.routes.Application.jsRoutes()" type="text/javascript"></script>
Finally, instead of using jQuery to make the AJAX call, you can use an object that is aware of the routing requirements:
jsRoutes.controllers.Application.index(id1, id2, etc).ajax( {
success : function (data, textStatus, jqXHR ) {
// do something on success
},
error: function (jqXHR, textStatus, errorThrown) {
// do something on error
},
complete: function(jqXHR, textStatus) {
// do something on complete
}
});
There's an additional benefit here in that if you change your index() route to use path parameters instead of query parameters, no change is required in the JavaScript - the routing object will handle this for you.
References
Scala: http://www.objectify.be/wordpress/?p=746
Java: http://www.objectify.be/wordpress/?p=734
Edit: For single-page apps
There are two approaches you can use for single-page applications.
1. Create a controller that exposes every route you want to access for AJAX. For example, for the routes file
GET /foo controllers.FooController.foo()
GET /bar controllers.BarController.bar()
you can have a (e.g.) JsRoutesController controller that has a function
def jsRoutes = Action { implicit request =>
Ok(Routes.javascriptRouter("jsRoutes")(
controllers.routes.javascript.FooController.foo)),
controllers.routes.javascript.BarController.bar))
.as("text/javascript")
}
Update your routes
GET /assets/js/routes controllers.JsRoutesController.jsRoutes()
and then access it using
<script src="/js/routes" type="text/javascript"></script>
or 2. The JS object name is specified in the jsRoutes function (Routes.javascriptRouter("jsRoutes") in the above example). You can have a jsRoutes function per controller which exposes a controller-specific JS routing object.
In FooController:
def jsRoutes = Action { implicit request =>
Ok(Routes.javascriptRouter("fooRoutes")(
controllers.routes.javascript.FooController.foo))
.as("text/javascript")
}
In BarController:
def jsRoutes = Action { implicit request =>
Ok(Routes.javascriptRouter("barRoutes")(
controllers.routes.javascript.BarController.bar))
.as("text/javascript")
}
Update your routes file
GET /assets/js/foo/routes controllers.FooController.jsRoutes()
GET /assets/js/bar/routes controllers.BarController.jsRoutes()
And in your page
<script src="/js/foo/routes" type="text/javascript"></script>
<script src="/js/bar/routes" type="text/javascript"></script>
Nothing in either approach requires Play 2's view layer.
Upvotes: 1