kgx
kgx

Reputation: 1195

Parameter and view naming collisions in Play/Scala templates

I am new to Play Framework and still trying to wrap my head around some things with the new Scala template engine.

Let's say I have the following package structure:

app/
app/controllers/Items.scala
app/models/Item.scala
app/views/layouts/page.scala.html
app/views/item/show.scala.html
app/views/item/details.scala.html  //partial

And this is my item/show template:

    @(item: Item, form: Form[Item])(implicit flash: Flash)
    @layout.page() {
        @*want to include details partial, wont work due to item param*@
        @item.details(item)
    }

Since including another template (e.g. including item/details above) is the exact same syntax as accessing a template parameter (e.g. item above), obviously this existing naming convention won't work without something changing.

I know I can rename my "app.views.item" package to "app.views.items", and rely on singular/plural forms to differentiate the view from the param name, but this does not seem like a very straightforward solution. Also what if I really want the parameter name to be the same as the view package?

One idea I have is to prepend all my views with an extra top level package:

 app/views/views/item/details.scala.html

So the include syntax would be @views.item.details(), but again this is obviously a hack.

What is a good way to avoid this issue? How can I better organize my code to avoid such naming collisions?

Most other template engines use operations like "include" or "render" to specify a partial include. I don't mean to offend anyone here, but is the Play Scala template engine syntax so terse that it actually dictates the organization of code?

Upvotes: 2

Views: 1286

Answers (1)

biesior
biesior

Reputation: 55798

3 solutions:

First

Typpicaly for partial templates you should use tags as described in the docs, where app/views/tags folder is a base:

file: app/views/tags/product.scala.html

in the templates (no initial import required in the parent view full syntax will allow you to avoid name-clash: @tags.packageName.tagName()):

<div id="container">
    @tags.product(item)
</div>

Of course in your case you can also use packages in the base folder

file: app/views/tags/item/product.scala.html

<div id="container">
    @tags.item.product(item)
</div>

I'm pretty sure that'll solve your problem.

Second

To avoid clash without changing package's name you can just rename the item in your view, also I recommend do not use a form name for the Form[T] as it can conflict with helpers:

@(existingItem: Item, existingItemForm: Form[Item])(implicit flash: Flash)
@layout.page() {
    @item.details(existingItem)
}

Third

If you'll fill your Form[Item] before passing to the view with given Item object, you don't need to pass both, as most probably you can get data from the form:

@(itemForm: Form[Item])(implicit flash: Flash)
@layout.page() {
    <div>Name of item is: @itemForm("name").value (this is a replacemnet for @@existingItem.name </div>
    @item.details(itemForm)
}

Of course in you product.scala.html you'll need to change the @(item: Item) param to @(itemForm: Form[Item])

Upvotes: 1

Related Questions