PlayFramework2 how to get current page's URL in view templates
Asked Answered
M

3

7

Originally posted and answered here:

https://groups.google.com/forum/#!topic/play-framework/s-ufMIbLz3c

But when I put:

<div>
 @if (request.uri == "/") { "Home Menu Selected" }
</div>

But I got:

'(' expected but ')' found.
Muskellunge answered 17/9, 2013 at 1:8 Comment(1)
As Karl wrote this is caused by a space between @if and ( that's the only reason and after removing the space it will be OK.Acetabulum
L
8

Assuming you have the request object in scope, sadly enough you just need to remove the space after "if". Play templates are pretty sensitive about spacing. Try:

<div>
 @if(request.uri == "/") { "Home Menu Selected" }
</div>
Lamoree answered 17/9, 2013 at 3:19 Comment(1)
@GovindSingh - Are you declaring request in your template? Check the linked post for how to wire it up in your view and controller: groups.google.com/d/msg/play-framework/s-ufMIbLz3c/vr9RP2MDpLkJLamoree
M
7

What you are trying to achieve is quite common, you are trying to show the current page in a menu by marking it as active.

Solution 1

You can indeed do what you did above. Add several @if conditions with string comparisons in your template.

@if(request.uri == "/"){ class="active" }

Solution 2

But I like to go a little further in the type safe architecture. I generally create an object containing a lot of constants :

object MenuContants {
    val HOME = "HOME"
    val CONTACT = "CONTACT"
}

And then I give those constants around in the templates. From sub-template to the master layout template :

@main("The title of my page", MenuConstants.HOME) {
    // the rest of my template
}

And then in your main template, do the comparison but no longer based on strings but on constants, which is type-safe.

@(title:String, contant:String) {
    @if(contant == MenuConstants.HOME) { class="active" }
}
Molybdous answered 17/9, 2013 at 7:10 Comment(3)
Nice to see a solution that adheres to the type safe approach.Anthology
For Solution 2, any string value (not just the ones defined in MenuContants) could be passed in to as "contant". This seems to require more ceremony, and provide more opportunities for mistakes (as someone in a rush might accidentally choose the wrong value from MenuContants). How is this solution more type safe?Tranquillity
You should use the constants defined in the class. But indeed, there is no restriction on it. Any String would work. However, if you really want to go further, you could create case classes extending a mother interface. But I dont' think it is worth it.Molybdous
K
0

To answer the question

The implicit Request, available to the templates, has all the details. Adapted to the example in the question I would use the following template function, which was tested in Play Framework 2.6. It does not require to maintain an enum but you still stay safe when updating your routes file and get compile errors when you rename the actions.

@*
  Usage:
  <div>
    @outputIf(routes.HomeController.index, "Home Menu Selected")
  </div>
*@
@outputIf(call:play.api.mvc.Call, text:String) = @{
  if (request.path.equals(call.path)) text else ""
}

Documentation

There is some documentation about reusable blocks: https://www.playframework.com/documentation/2.6.x/ScalaTemplates#Declaring-reusable-blocks

play.api.mvc.Call API: https://www.playframework.com/documentation/2.6.x/api/scala/index.html#play.api.mvc.Call

play.api.mvc.Request API: https://www.playframework.com/documentation/2.6.x/api/scala/index.html#play.api.mvc.Request

My intention (rendering Navigation)

I recently stumbled across a related Problem and after i found nothing specific to my problem on the web aside from this post, i'd like to share my final solution with you.

I'd wanted to render navigation list items with an 'active' class if the current url of the page is equal to the list items' url but neither did I like to just compare the url-string for equality as it may change and would give no compile errors - nor did I accept to create and update an enum every time I add a new navigation item (as I have an increasing amount of them including children). However I still wanted to stay safe when changing the routes urls at some time. In Play Framework 2.6 I solved the problem using this reusable function within the main.scala.html template:

@*
  Renders a list item <li> with active-class containing a link element
  Usage:
  @navItem(routes.HomeController.index, "Home")
  @navItem(routes.HomeController.index, "Home", "nav-home")
  @navItem(routes.HomeController.index, "Home", activeClass="selected")
  @navItem(routes.HomeController.index, "<span>Home</span>", "nav-home nav-element", "selected active")
*@
@navItem(call:play.api.mvc.Call, linkContent:String, cls:String="", activeClass:String="active") = @{
  val hrefAttr = "href='"+call+"'"
  val classNames = if (request.path.equals(call.path)) activeClass + " " + cls else cls
  val classAttr = if(classNames.length>0) " class='"+classNames+"'" else ""
  val linkHtml = Html(linkContent)
  Html("<li"+classAttr+"><a "+hrefAttr+">"+linkHtml+"</a></li>")
}

the comment example renders to the following in case the Homepage is requested:

<li class="active"><a href="/">Home</a></li>
<li class="active nav-home"><a href="/">Home</a></li>
<li class="selected"><a href="/">Home</a></li>
<li class="selected active nav-home nav-element"><a href="/"><span>Home</span></a></li>
Kartis answered 16/11, 2018 at 14:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.