play2의 모든 곳에서 매개변수 전달을 방지하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com//questions/9629250

  •  09-12-2019
  •  | 
  •  

문제

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
}

따라서 템플릿은 종종 최소한 이 암시적 매개변수를 수신하므로 이를 포함하는 더 풍부한 값으로 바꿀 수 있습니다.당신의 메뉴.당신은 다음을 사용하여 그렇게 할 수 있습니다 액션 구성 플레이 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)

Java에는 Scala의 암시적 메커니즘이나 이와 유사한 메커니즘이 없으므로 템플릿 매개변수를 명시적으로 전달하는 것을 피하려는 경우 가능한 방법은 해당 매개변수를 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")

그래서 가독성도 좋고 좋습니다.

이것이 내가 가기로 선택한 길이다.스티안 - 좋은 조언이에요.모든 답변을 보려면 아래로 스크롤하는 것이 중요하다는 것을 증명합니다.:)

HTML 변수 전달

Html 변수를 전달하는 방법을 아직 찾지 못했습니다.

@(제목:문자열,내용:Html)

그러나 나는 그것들을 블록으로 전달하는 방법을 알고 있습니다.

@(제목:문자열)(내용: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의 일반적인 사용 사례 it 템플릿 상속입니다.

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)}

페이지에 "login stuff..."가 두 번 표시됩니다. 왜냐하면 동일한 키를 입력할 때 두 번째로 나타나는 값을 put이 반환하기 때문입니다.(Java 문서에 서명 넣기 참조)

스칼라는 맵을 수정하는 더 나은 방법을 제공합니다

@{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>
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top