Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing layout template in runtime

In Kohana (PHP framework) the layout is implemented through Template_Controller which continas a member variable called $template, which serves as layout view. Then in the action method you can populate the $template with further sub-views, usually the content view. (http://forum.kohanaframework.org/discussion/3612/kohana-layout-system/p1)

This allows me to change the layout "theme" in the runtime. It is useful for multitenant system, where a tenant can select their own theme (two col, three col, etc.)

How can I achieve that in playframework 2 Scala, with Scala template engine? In other words, I'd like to have multiple layout templates in which a tenant can select from. The controller then renders the layout template and the action specific content template.

Something like (Controller's action pseudocode):

  1. Based on user, retrieve the layout theme (a name stored in string in a database, and has corresponding mapping view file).
  2. Render the action specific content view.
  3. Render layout view obtained from (1) along with the (2).

Note: for each action, the layout theme may change per user but the content view remains same.

In it's documentation (http://www.playframework.com/documentation/2.1.1/ScalaTemplateUseCases)
the content template, say, the index.scala.html, includes a call to the main which is defined in main.scala.html, the layout template. In other words, it is hard coded, thus index.scala.html is tightly coupled to main.scala.html.

I though about calling the main from the controller using reflection, and then passing the content.

An alternative would be to use a interpreted template engine such as Scalate.

Any suggestion?

like image 965
Young Suk Ahn Avatar asked Jun 24 '13 12:06

Young Suk Ahn


1 Answers

I see 2 options to accomplish what you're after. The first would be to pass a theme parameter to your templates (i.e. something to tell the called template which theme/layout to use) and use that parameter to conditionally call a layout template. The second would be to handle the condition inside the controller by returning the appropriate view based on the selected theme.

Option 1

In your action you will want to pass some value to your template to indicate which theme to use.

def index = Action {
  Ok(views.html.index("two-col"))
}

Then in your index.scala.html you would do something like this:

@(theme: String)

@content = {
  <h1>Action Specific Content</h1>
}

@if("two-col" eq theme) {
  @twoCol("title")(content)
} else {
  @main("title")(content)
}

This would expect there to be a twoCol.scala.html template, such as:

@(title: String)(content: Html)
<!DOCTYPE html>
<html>
  ...
  <body>
    <h1>Two Column</h1>
    @content
  </body>
</html>

Note: You can also pass the theme using an implicit parameter, see this SO question. This would alleviate the need to explicitly pass it the template on every render.

Option 2

This would be as simple as the following in your controller, but would possibly require much more repeated code in the view templates.

def index = Action {
  var theme = ...
  ...
  if (theme eq 'tow-col') {
    Ok(views.html.twocol.index("two-col"))
  } else {
    Ok(views.html.default.index())
}

This assumes there is a twocol and default package in /app/views that have an index.scala.html.

Additional Comments

As you can tell from option 1, index.scala.html is not tightly coupled with main.scala.html. You can replace the call to main with a call to any other template, or even no template.

FWIW, I would go with option 1 and It would possibly evolve into a better solution.

like image 61
Jonathan Dixon Avatar answered Jan 04 '23 11:01

Jonathan Dixon