Search code examples
playframeworkplayframework-2.0scala-2.10

Passing Argument to main.scala.html


Problem (which works):

I am using Play 2.1.x version in my application. My application has a static header/toolbar which has SignIn/SignOut links. Instead of repeating status header html code in all the templates, I created that in main.scala.html template. Now to change SignIn/SignOut status, I need to pass an argument to main.scala.html. If I am right this is the parent template for all the templates we use in play, so if we add any argument to main template, it has to be provided by the child templates like for e.g index, login etc (you name it)

So far I am able to do this by creating an implicit def in a trait and let controller implement the trait like this (mixing composition...)

class ApplicationController extends Controller with ImplicitSession{

}

trait ImplicitSession {
implicit def status(implicit session: play.api.mvc.Session): Boolean = {
session.get("session") match {
  case None => false
  case _ => true
 }
}

and let my all the other controller extends ApplicationController instead of only play Controller

object LoginController extends ApplicationController{...}

Now My templates arguments look like this...

Main Template

@(title: String)(content: Html)(implicit status : Boolean)

other templates like login...

@(message: String)(implicit status : Boolean)

This works Fine but I am not happy with the solution because it force me to pass this extra implicit parameter to all my templates

I really want to avoid passing this implicit parameter to my all child templates

because I feel this is a redundant code and indeed not a good programming practice. So Looks like I didn't get this concept clear in my mind

Also compiler force me to put Action{ implicit request => to all the controller I am creating otherwise it just throw an compilation error

I checked the sample code provided in the play bundle, they are good but my doubt of the flow is not clear..

Thanks in Advance


Solution

  • I really want to avoid passing this implicit parameter to my all child templates

    The answer is that it is not possible to avoid (sadly).

    Here's an excellent blog describing this by James Roper (who is a Play Framework developer at Typesafe).

    I use this pattern as well but I have preferred to use the name ApplicationContext for the case class that I pass along to all my views.

    case class ApplicationContext(user: Option[User], moreData: SomeData)
    
    trait ApplicationController extends Controller {
      implicit def context[A](implicit request: Request[A]): ApplicationContext = {
        // Find all necessary data needed
        ...
        ApplicationContext(user, data)
      }
    }
    

    And my views then typically takes the following parameters (including Lang for use of i18n):

    @(message: String)(implicit lang: Lang, context: ApplicationContext)