play1에서는 일반적으로 모든 데이터가 동작하며 뷰에서 직접 사용합니다. 뷰에서 매개 변수를 명시 적으로 선언 할 필요가 없으므로 매우 쉽습니다.
그러나 play2에서 request
뷰 헤드에 모든 매개 변수 (포함 ) 를 선언해야한다는 것을 알았 습니다. 모든 데이터를 동작으로 가져와 뷰에 전달하는 것은 매우 지루합니다.
예를 들어, 프론트 페이지의 데이터베이스에서로드 된 메뉴를 표시해야하는 경우 다음에서 정의해야합니다 main.scala.html
.
@(title: String, menus: Seq[Menu])(content: Html)
<html><head><title>@title</title></head>
<body>
<div>
@for(menu<-menus) {
<a href="#">@menu.name</a>
}
</div>
@content
</body></html>
그런 다음 모든 하위 페이지에서 선언해야합니다.
@(menus: Seq[Menu])
@main("SubPage", menus) {
...
}
그런 다음 메뉴를 가져 와서 모든 작업에서 볼 수 있도록 전달해야합니다.
def index = Action {
val menus = Menu.findAll()
Ok(views.html.index(menus))
}
def index2 = Action {
val menus = Menu.findAll()
Ok(views.html.index2(menus))
}
def index3 = Action {
val menus = Menu.findAll()
Ok(views.html.index(menus3))
}
지금은의 매개 변수가 하나뿐입니다 main.scala.html
.
그래서 마침내 모든 것을 Menu.findAll()
직접보기 로 결정했습니다 .
@(title: String)(content: Html)
<html><head><title>@title</title></head>
<body>
<div>
@for(menu<-Menu.findAll()) {
<a href="#">@menu.name</a>
}
</div>
@content
</body></html>
그것이 좋거나 권장되는지 모르겠습니다. 이에 대한 더 나은 해결책이 있습니까?
답변
제 생각에는 템플릿이 정적으로 입력된다는 사실은 실제로 좋은 것입니다. 컴파일하면 템플릿을 호출해도 실패하지 않을 것입니다.
그러나 실제로 호출 사이트에 약간의 상용구를 추가합니다. 그러나 정적 타이핑 이점을 잃지 않고 줄일 수 있습니다 .
스칼라에서는 두 가지 방법으로 작업 구성을 통해 또는 암시 적 매개 변수를 사용합니다. Java에서는 Http.Context.args
맵을 사용하여 유용한 값을 저장하고 템플릿 매개 변수로 명시 적으로 전달하지 않고도 템플릿에서 값을 검색하는 것이 좋습니다 .
암시 적 매개 변수 사용
장소 menus
당신의 말에 매개 변수를 main.scala.html
템플릿 매개 변수와 “암시”로 표시 :
@(title: String)(content: Html)(implicit menus: Seq[Menu])
<html>
<head><title>@title</title></head>
<body>
<div>
@for(menu<-menus) {
<a href="#">@menu.name</a>
}
</div>
@content
</body>
</html>
이제이 기본 템플릿을 호출 menus
하는 main
템플릿이있는 경우 이러한 템플릿에서 암시 적 매개 변수로 선언 된 경우 Scala 컴파일러 에서 템플릿으로 암시 적으로 매개 변수를 전달할 수 있습니다.
@()(implicit menus: Seq[Menu])
@main("SubPage") {
...
}
그러나 컨트롤러에서 암시 적으로 전달하려면 템플릿을 호출하는 범위에서 사용 가능한 암시 적 값으로 제공해야합니다. 예를 들어 컨트롤러에서 다음 메소드를 선언 할 수 있습니다.
implicit val menu: Seq[Menu] = Menu.findAll
그런 다음 작업에서 다음을 작성할 수 있습니다.
def index = Action {
Ok(views.html.index())
}
def index2 = Action {
Ok(views.html.index2())
}
이 방법에 대한 자세한 내용은 이 블로그 게시물 및 이 코드 샘플 에서 확인할 수 있습니다 .
업데이트 :이 패턴을 보여주는 멋진 블로그 게시물도 여기 에 작성 되었습니다 .
활동 구성 사용
실제로, RequestHeader
값을 템플릿 에 전달하는 것이 종종 유용합니다 (예 : 이 샘플 참조 ). 암시 적 요청 값을 수신하는 조치를 쉽게 작성할 수 있으므로 컨트롤러 코드에 상용구를 추가하지 않습니다.
def index = Action { implicit request =>
Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}
따라서 템플릿에는 종종이 암시 적 매개 변수가 수신되기 때문에 메뉴를 포함하는 더 풍부한 값으로 대체 할 수 있습니다. Play 2 의 액션 구성 메커니즘을 사용하면 됩니다.
이렇게하려면 Context
기본 요청을 래핑 하여 클래스 를 정의해야합니다 .
case class Context(menus: Seq[Menu], request: Request[AnyContent])
extends WrappedRequest(request)
그런 다음 다음 ActionWithMenu
방법을 정의 할 수 있습니다 .
def ActionWithMenu(f: Context => Result) = {
Action { request =>
f(Context(Menu.findAll, request))
}
}
다음과 같이 사용할 수 있습니다 :
def index = ActionWithMenu { implicit context =>
Ok(views.html.index())
}
템플릿에서 컨텍스트를 암시 적 매개 변수로 사용할 수 있습니다. 예 main.scala.html
:
@(title: String)(content: Html)(implicit context: Context)
<html><head><title>@title</title></head>
<body>
<div>
@for(menu <- context.menus) {
<a href="#">@menu.name</a>
}
</div>
@content
</body>
</html>
작업 구성을 사용하면 템플릿에 필요한 모든 암시 적 값을 단일 값으로 집계 할 수 있지만 반면에 유연성은 떨어질 수 있습니다.
Http.Context 사용 (자바)
Java에는 스칼라의 암시 적 메커니즘 또는 이와 유사한 기능이 없으므로 템플릿 매개 변수를 명시 적으로 전달하지 않으려면 Http.Context
요청 기간 동안 만 존재 하는 오브젝트에 템플릿을 저장하는 것이 가능합니다 . 이 객체 args
는 유형 의 값을 포함합니다 Map<String, Object>
.
따라서 문서에 설명 된대로 인터셉터를 작성하여 시작할 수 있습니다 .
public class Menus extends Action.Simple {
public Result call(Http.Context ctx) throws Throwable {
ctx.args.put("menus", Menu.find.all());
return delegate.call(ctx);
}
public static List<Menu> current() {
return (List<Menu>)Http.Context.current().args.get("menus");
}
}
정적 메소드는 현재 컨텍스트에서 메뉴를 검색하는 간단한 방법입니다. 그런 다음 컨트롤러에 Menus
조치 인터셉터 와 혼합되도록 주석을 답니다 .
@With(Menus.class)
public class Application extends Controller {
// …
}
마지막 menus
으로 다음과 같이 템플릿 에서 값을 검색하십시오 .
@(title: String)(content: Html)
<html>
<head><title>@title</title></head>
<body>
<div>
@for(menu <- Menus.current()) {
<a href="#">@menu.name</a>
}
</div>
@content
</body>
</html>
답변
내가하는 방법은 내 탐색 / 메뉴에 대한 새 컨트롤러를 만들고보기에서 호출하는 것입니다.
그래서 당신은 정의 할 수 있습니다 NavController
:
object NavController extends Controller {
private val navList = "Home" :: "About" :: "Contact" :: Nil
def nav = views.html.nav(navList)
}
nav.scala.html
@(navLinks: Seq[String])
@for(nav <- navLinks) {
<a href="#">@nav</a>
}
그런 다음 기본보기에서 다음과 같이 호출 할 수 있습니다 NavController
.
@(title: String)(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
</head>
<body>
@NavController.nav
@content
</body>
</html>
답변
나는 stian의 대답을지지합니다. 이것은 결과를 얻는 매우 빠른 방법입니다.
방금 Java + Play1.0에서 Java + Play2.0으로 마이그레이션했으며 템플릿은 지금까지 가장 어려운 부분이며 기본 템플릿 (제목, 헤드 등)을 구현하는 가장 좋은 방법은 Http를 사용하는 것입니다. .문맥.
태그를 사용하여 얻을 수있는 매우 좋은 구문이 있습니다.
views
|
\--- tags
|
\------context
|
\-----get.scala.html
\-----set.scala.html
get.scala.html은 다음과 같습니다.
@(key:String)
@{play.mvc.Http.Context.current().args.get(key)}
set.scala.html은 다음과 같습니다.
@(key:String,value:AnyRef)
@{play.mvc.Http.Context.current().args.put(key,value)}
모든 템플릿에서 다음을 작성할 수 있음을 의미합니다.
@import tags._
@context.set("myKey","myValue")
@context.get("myKey")
따라서 매우 읽기 쉽고 좋습니다.
이것이 내가 가기로 선택한 방식입니다. stian-좋은 조언. 모든 답변을 보려면 아래로 스크롤하는 것이 중요합니다. 🙂
HTML 변수 전달
HTML 변수를 전달하는 방법을 아직 알지 못했습니다.
@ (제목 : 문자열, 내용 : Html)
그러나 블록으로 전달하는 방법을 알고 있습니다.
@ (제목 : 문자열) (content : Html)
set.scala.html을
@(key:String)(value:AnyRef)
@{play.mvc.Http.Context.current().args.put(key,value)}
이렇게하면 HTML 블록을 전달할 수 있습니다.
@context.set("head"){
<meta description="something here"/>
@callSomeFunction(withParameter)
}
편집 : 내 “세트”구현으로 부작용
일반적인 유스 케이스는 Play에서 템플릿 상속입니다.
base_template.html이 있고 base_template.html을 확장하는 page_template.html이 있습니다.
base_template.html은 다음과 같습니다
<html>
<head>
<title> @context.get("title")</title>
</head>
<body>
@context.get("body")
</body>
</html>
페이지 템플릿은 다음과 같이 보일 수 있습니다.
@context.set("body){
some page common context here..
@context.get("body")
}
@base_template()
그런 다음 다음과 같은 페이지가 있습니다 (login_page.html로 가정)
@context.set("title"){login}
@context.set("body"){
login stuff..
}
@page_template()
여기서 주목해야 할 것은 “body”를 두 번 설정한다는 것입니다. “login_page.html”과 “page_template.html”에 한 번
위에서 제안한 것처럼 set.scala.html을 구현하는 한 부작용이 발생하는 것으로 보입니다.
@{play.mvc.Http.Context.current().put(key,value)}
put은 같은 키를 두번 째 켤 때 튀어 나오는 값을 반환하기 때문에 페이지에 “login stuff …”가 두 번 표시되므로 (자바 문서에 서명 넣기 참조).
스칼라는지도를 수정하는 더 좋은 방법을 제공합니다
@{play.mvc.Http.Context.current().args(key)=value}
이 부작용을 일으키지 않습니다.
답변
Java를 사용 중이고 인터셉터를 작성하지 않고 @With 주석을 사용하지 않고 가능한 가장 간단한 방법을 원하는 경우 템플리트에서 직접 HTTP 컨텍스트에 액세스 할 수도 있습니다.
예를 들어 템플릿에서 사용 가능한 변수가 필요한 경우 다음을 사용하여 HTTP 컨텍스트에 추가 할 수 있습니다.
Http.Context.current().args.put("menus", menus)
그런 다음 다음을 사용하여 템플릿에서 액세스 할 수 있습니다.
@Http.Context.current().args.get("menus").asInstanceOf[List<Menu>]
분명히 Http.Context.current (). args.put ( “”, “”) 메서드를 사용하면 인터셉터를 사용하는 것이 더 좋지만 간단한 경우에는 트릭을 수행 할 수 있습니다.
답변
Stian의 답변에서 다른 접근법을 시도했습니다. 이것은 나를 위해 작동합니다.
자바 코드
import play.mvc.Http.Context;
Context.current().args.put("isRegisterDone", isRegisterDone);
HTML 템플릿 헤드
@import Http.Context
@isOk = @{ Option(Context.current().args.get("isOk")).getOrElse(false).asInstanceOf[Boolean] }
그리고 같은 사용
@if(isOk) {
<div>OK</div>
}