Danny Boyd
Danny Boyd

Reputation: 317

Dynamic routes on Play Framework 2.1 templates

Taking the Java computer-database sample as a starting point in Play! 2.1 I'm developing a CRUD admin backend for about 20 models. The problem I'm facing is the repetition at the template level of the same functions over and over.

I have tried to refactor the link function as:

@****************************************
* Helper generating navigation links    *
****************************************@
@link(newPage:Int, newSortBy:String) = @{
    // Generate the link
    controllers.admin.routes.Model.index(newPage, "id", newSortBy)
}

To something like

@(controller: Any, newPage: Int, newSortBy: String)
any.index(newPage, "id", newSortBy)

Since the reverse routers do not inherit from any specific class, I can't do it in a generic way. And the previous code doesn't work since I don't do a type casting (I guess since the compiler error is "value index is not a member of Any")

Is there any way I can get a list of the defined routes during runtime? Doesn't matter if it is in the Scala template or the Java side, that way I can pass a controller name and get the correct reverse router to create the URL.

If it is not possible, then what is the correct way to refactor all that code that repeats over and over the templates, specifically the ones that depend on the ReverseRouters.

Upvotes: 4

Views: 3177

Answers (1)

Danny Boyd
Danny Boyd

Reputation: 317

I haven't found a way to get a list of the routers and reverse routers dynamically, but I have found a way to limit the amount of boiler plate code and be able to refactor template functions to their own files, making the code more manageable.

I have found that Scala is just not another way of saying Java!!! Specifically you can pass functions as a parameter to a function (http://oldfashionedsoftware.com/2008/08/23/fun-with-scala-functions/), so you can pass a piece of code to be executed on the subtemplate from the main template (kind of DI).

In the example I posted the problem was passing the Reverse Controller to subtemplates from the main template, but the "Reverse Controllers" do not extend any specific object. The code in the samples is:

template.scala.html

...
...
@****************************************
* Helper generating navigation links    *
****************************************@
@link(newPage:Int, newSortBy:String) = @{
    // Generate the link
    controllers.admin.routes.MyController.index(newPage, "id", "asc")
}
...
...
@**********************************
* Helper generating table headers *
***********************************@
@header(key:String, title:String) = {
    <th class="">
        <a href="@link(0, key)">@title</a>
    </th>
}
...
...

To refactor the header function to its own template, I must be able call the the link function. Using includes from the refactored file will not work since the reverse controller is hardcoded in the link function. The solution is to extract the header function to a subtemplate and define a parameter of the template to be a function:

views/utils/header.scala.html

@(key: String, title:String, link: (Int) => Html)
    <th class="@key">
        <a href="@link(0)">@title</a>
    </th>

And now in the template:

views/admin/template.scala.html

...
@import utils._
...
@link(newPage:Int) = {
    @controllers.admin.routes.MyController.index(newPage, "id", "asc")
}
...
@header("key", "title", link _)
...

Now, I only have to define the link function for each one of the main templates and pass it as a parameter to any subtemplates that need it. Not completely clean but reduces a lot the boiler plate and eases the template changes.

Hope it helps others. Better solution anyone?

Upvotes: 4

Related Questions