Как избежать передачи параметров везде в play2?


в 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>

Я не знаю, если это хорошо или рекомендуется, есть ли лучшее решение для этого?

5 122

5 ответов:

на мой взгляд, тот факт, что шаблоны статически типизированы, на самом деле хороший вещь: вы гарантируете, что вызов вашего шаблона не потерпит неудачу, если он компилируется.

тем не менее, он действительно добавляет некоторые шаблонные на вызывающих сайтах. Но вы можете уменьшить его (без потери статических преимуществ ввода).

в Scala я вижу два способа его достижения: с помощью композиции действий или с помощью неявных параметров. В Java, я предлагаю использовать элемент Http.Context.args карта для хранения полезных значений и извлечения их из шаблонов без необходимости явно передавать в качестве параметров шаблонов.

использование неявных параметров

место в конце 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.Контекст (Ява)

так как 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.скала.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>

Если вы используете Java и просто хотите самый простой способ без необходимости писать перехватчик и использовать аннотацию @With, вы также можете получить доступ к контексту HTTP непосредственно из шаблона.

Http.Context.current().args.put("menus", menus)

затем вы можете получить доступ к нему из шаблона с:

@Http.Context.current().args.get("menus").asInstanceOf[List<Menu>]

очевидно, если вы засоряете свои методы Http.Контекст.текущий.)(параметр args.put ("","") вы лучше используете перехватчик, но для простых случаев это может сделать трюк.

Я поддерживаю ответ Стиана. Это очень быстрый способ получить результаты.

Я только что перешел с Java + Play1. 0 на Java + Play2.0, и шаблоны-самая сложная часть до сих пор, и лучший способ, который я нашел, чтобы реализовать базовый шаблон (для заголовка, головы и т. д..) осуществляется с помощью Http.Контекст.

есть очень хороший синтаксис, который вы можете достичь с помощью тегов.

views
  |
  \--- tags
         |
         \------context
                  |
                  \-----get.scala.html
                  \-----set.scala.html

где сделать.скала.HTML-код :

@(key:String)
@{play.mvc.Http.Context.current().args.get(key)}

и установить.скала.формат html это:

@(key:String,value:AnyRef)
@{play.mvc.Http.Context.current().args.put(key,value)}

означает, что вы можете написать следующее В любом шаблоне

@import tags._
@contest.set("myKey","myValue")
@context.get("myKey")

так что это очень читабельно и приятно.

это путь, который я выбрал, чтобы пойти. Стиан - хороший совет. Доказывает, что важно прокрутить вниз, чтобы увидеть все ответы. :)

передача переменных HTML

Я еще не понял, как передать переменные Html.

@(название: строка, содержание: Html)

однако, я знаю, как передать их как блок.

@(название: строка) (содержание:Html)

так что вы можете заменить набор.скала.HTML с помощью

@(key:String)(value:AnyRef)
@{play.mvc.Http.Context.current().args.put(key,value)}

таким образом, Вы можете передать Html блоки, как так

@context.set("head"){ 
     <meta description="something here"/> 
     @callSomeFun(withParameter)
}

EDIT: побочный эффект с моей реализацией "Set"

распространенный вариант использования наследования шаблонов в игре.

у вас есть base_template.html, а затем у вас есть page_template.html, который расширяет base_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()

важно отметить, что вы установили "тело" дважды. Один раз в "login_page.html", а затем в " page_template.формат HTML."

кажется,что это вызывает побочный эффект, пока вы реализуете набор.скала.формат html как я и предлагал выше.

@{play.mvc.Http.Context.current().put(key,value)}

как страница покажет " логин вещи..."дважды, потому что put возвращает значение, которое выскакивает во второй раз, когда мы помещаем тот же ключ. (см. put signature in java docs).

Scala предоставляет лучший способ изменить карту

@{play.mvc.Http.Context.current().args(key)=value}

который не вызывает этот побочный эффект.

из ответа Стиана я попробовал другой подход. Это работает для меня.

В КОДЕ JAVA

import play.mvc.Http.Context;
Context.current().args.put("isRegisterDone", isRegisterDone);

В HTML ШАБЛОНЕ HEAD

@import Http.Context
@isOk = @{ Option(Context.current().args.get("isOk")).getOrElse(false).asInstanceOf[Boolean] } 

И ИСПОЛЬЗОВАТЬ КАК

@if(isOk) {
   <div>OK</div>
}