Scala / Play:动态加载模板

时间:2013-11-28 20:01:49

标签: scala templates playframework playframework-2.0 playframework-2.2

我有这个Scala / Play应用程序,我必须通过AJAX获取一堆模板。我现在正在做这样的事情:

def home = Action {
    Ok(views.html.home())
}

def about = Action {
    Ok(views.html.about())
}

def contact = Action {
    Ok(views.html.contact())
}

//etc

但这只是为每个模板创建一个动作。我可以这样做吗:

def loadTemplate(templateName) = Action {
    //Load template from "views" with name being value of parameter templateName
}

这可以在Play Framework上使用吗?如果是,那怎么样?

Play Framework 2.2.1 / Scala 2.10.3 / Java 8 64bit

更新:我原来的问题可能会被误解。我不想编译模板,我想以更动态的方式获取已编译的模板。

UPDATE2:我认为我发现了一些非常接近的东西,如果不是我需要的on this answer,但它是用Java编写的,我需要它在Scala中。

5 个答案:

答案 0 :(得分:3)

使用scala反射:

object Application extends Controller {

  import reflect.runtime.universe._
  val currentMirror = runtimeMirror(Play.current.classloader)
  val packageName = "views.html."

  def index(name: String) = Action {
    val templateName = packageName + name

    val moduleMirror = currentMirror.reflectModule(currentMirror.staticModule(templateName))
    val methodSymbol = moduleMirror.symbol.typeSignature.declaration(newTermName("apply")).asMethod

    val instanceMirror = currentMirror.reflect(moduleMirror.instance)    
    val methodMirror = instanceMirror.reflectMethod(methodSymbol)

    Ok(methodMirror.apply().asInstanceOf[Html])
 }

 }

答案 1 :(得分:2)

基于@Nilanjan回答和播放2.4 scala-2.11.7

  def page(page: String) = Action.async { implicit request =>
    Future.successful(Ok(loadTemplate(page)))
  }

  import reflect.runtime.universe._
  import play.api._
  import play.twirl.api.Html
  val currentMirror = runtimeMirror(Play.current.classloader)
  val packageName = "views.html."
  private def loadTemplate(name: String) = {
    val templateName = packageName + name
    val moduleMirror = currentMirror.reflectModule(currentMirror.staticModule(templateName))
    val methodSymbol = moduleMirror.symbol.info.member(TermName("apply")).asMethod
    val instanceMirror = currentMirror.reflect(moduleMirror.instance)
    val methodMirror = instanceMirror.reflectMethod(methodSymbol)
    methodMirror.apply().asInstanceOf[Html]
  }

答案 2 :(得分:0)

允许任何文本的搜索视图(出于安全原因)并不是一个好主意,因为这样可以在param中传递,相反,您可以使用match语句轻松解决此问题 - 它将允许您限制请求只允许查看并且也会处理错误的请求,可能Scala极客可以演示更好的代码,但这对你开箱即用:

def loadTemplate(templateName: String) = Action {

  templateName match {
    case "about" => Ok(about())
    case "home" => Ok(home())
    case "contact" => Ok(contact())
    case _ => NotFound(notFoundView())
  }

}

路线:

GET  /load-template/:templateName   controllers.Application.loadTemplate(templateName)

其他好处是您可以从请求中获取其他数据并将其传递到已解析的视图,具体取决于templateName param

答案 3 :(得分:0)

在模板文件中

general_template.scala.html

@(templateName : String = "none")

@templateName match {       

 case "about" => { //html elements}

 case "home" => {//html elements}

 case "contact" => {//html elements}

 case _ => { }

}

并在您的控制器中

def loadTemplate(templateName) = Action {
    Ok(views.html.general_template(templateName))
}

答案 4 :(得分:0)

再来一次! Scala 2.12和Play 2.6采用了@Nilanjan答案(已弃用至DI play.api.Play.current,TermName和声明)。对于应用程序,我必须使用注入器,因为@Inject()(app:Application)导致循环依赖

class HomeController @Inject()(injector: Injector, cc: ControllerComponents) extends AbstractController(cc) {
def index(name: String = "index") = Action { implicit request: Request[AnyContent] =>
import reflect.runtime.universe._

val app = injector.instanceOf[Application]
val currentMirror = runtimeMirror(app.classloader)
val packageName = "views.html."

val templateName = packageName + name

val moduleMirror = currentMirror.reflectModule(currentMirror.staticModule(templateName))
val methodSymbol = moduleMirror.symbol.typeSignature.decl(TermName("apply")).asMethod

val instanceMirror = currentMirror.reflect(moduleMirror.instance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)

Ok(methodMirror.apply("some","content").asInstanceOf[Html])

}