Play2'de her yerde parametrelerin geçmesini nasıl önleyebilirim?


125

Play1'de genellikle tüm verileri eylemlerde alırım, bunları doğrudan görünümlerde kullanırım. Görünüşte parametreleri açıkça bildirmemize gerek olmadığından, bu çok kolaydır.

Ama oyun2'de, tüm parametreleri (dahil request) görüşlerin başında beyan etmemiz gerektiğini anladım, tüm verileri eylemlerde alıp görüşlere aktarmak çok sıkıcı olacak.

Örneğin, veritabanından yüklenen menüleri ön sayfada görüntülemem gerekirse, bunu şu şekilde tanımlamam gerekir 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>

O halde her alt sayfada beyan etmeliyim:

@(menus: Seq[Menu])

@main("SubPage", menus) {
   ...
}

Sonra menüleri almam ve her eylemde görüntülemek için geçmem gerekiyor:

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

Şimdilik sadece bir parametre main.scala.htmlvar, ya çok varsa?

Sonunda, Menu.findAll()doğrudan görüş açısına karar verdim :

@(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>

İyi mi tavsiye mi bilmiyorum, bunun için daha iyi bir çözüm var mı?


Belki play2 lift'in parçacıkları gibi bir şey eklemeli
Freewind

Yanıtlar:


229

Kanımca, şablonların statik olarak yazılmış olması aslında iyi bir şey: derlenirse şablonunuzu çağırmanın başarısız olmayacağı garantidir.

Ancak, gerçekten arayan sitelere bazı standart metinler ekler. Ancak bunu azaltabilirsiniz (statik yazım avantajlarını kaybetmeden).

Scala'da, bunu başarmanın iki yolunu görüyorum: eylem bileşimi yoluyla veya örtük parametreler kullanarak. Java'da,Http.Context.args yararlı değerleri depolamak ve bunları açık bir şekilde şablon parametreleri olarak geçirmek zorunda kalmadan şablonlardan almak haritayı .

Örtük parametreleri kullanma

Yerleştirin menussenin sonunda parametreyi main.scala.htmlşablon parametreleri ve “örtük” olarak işaretleyin:

@(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>

Şimdi, bu ana şablonu çağıran şablonlarınız varsa, parametrenin, bu şablonlarda da örtük bir parametre olarak bildirilmesi durumunda Scala derleyicisi tarafından şablona menusörtük olarak iletilmesini sağlayabilirsiniz main:

@()(implicit menus: Seq[Menu])

@main("SubPage") {
  ...
}

Ancak, denetleyicinizden örtük olarak aktarılmasını istiyorsanız, şablonu çağırdığınız kapsamda bulunan örtük bir değer olarak sağlamanız gerekir. Örneğin, denetleyicinizde aşağıdaki yöntemi bildirebilirsiniz:

implicit val menu: Seq[Menu] = Menu.findAll

Ardından, eylemlerinizde yalnızca aşağıdakileri yazabileceksiniz:

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

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

Bu yaklaşımla ilgili daha fazla bilgiyi bu blog gönderisinde ve bu kod örneğinde bulabilirsiniz .

Güncelleme : Bu kalıbı gösteren güzel bir blog yazısı da burada yazılmıştır .

İşlem kompozisyonunu kullanma

Aslında, RequestHeaderdeğeri şablonlara aktarmak genellikle yararlıdır (örneğin, bu örneğe bakın ). Bu, denetleyici kodunuza çok fazla standart şablon eklemez çünkü örtük bir istek değeri alan eylemleri kolayca yazabilirsiniz:

def index = Action { implicit request =>
  Ok(views.html.index()) // The `request` value is implicitly passed by the compiler
}

Bu nedenle, şablonlar genellikle en azından bu örtük parametreyi aldığından, onu örneğin menülerinizi içeren daha zengin bir değerle değiştirebilirsiniz. Bunu Play 2'nin eylem oluşturma mekanizmasını kullanarak yapabilirsiniz .

Bunu yapmak için Contextsınıfınızı tanımlamanız ve temel bir isteği sarmalamanız gerekir:

case class Context(menus: Seq[Menu], request: Request[AnyContent])
        extends WrappedRequest(request)

Ardından aşağıdaki ActionWithMenuyöntemi tanımlayabilirsiniz :

def ActionWithMenu(f: Context => Result) = {
  Action { request =>
    f(Context(Menu.findAll, request))
  }
}

Hangisi şu şekilde kullanılabilir:

def index = ActionWithMenu { implicit context =>
  Ok(views.html.index())
}

Ve bağlamı şablonlarınızda örtük bir parametre olarak alabilirsiniz. Örneğin 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>

Eylem bileşimini kullanmak, şablonlarınızın gerektirdiği tüm örtük değerleri tek bir değerde toplamanıza olanak tanır, ancak diğer yandan biraz esnekliği kaybedebilirsiniz ...

Http.Context (Java) Kullanma

Java, Scala'nın implicits mekanizmasına veya benzerine sahip olmadığından, şablon parametrelerini açık bir şekilde iletmekten kaçınmak istiyorsanız, olası bir yol onları Http.Contextyalnızca bir istek süresince yaşayan nesnede saklamaktır . Bu nesne bir tür argsdeğeri içerir Map<String, Object>.

Böylece, dokümantasyonda açıklandığı gibi bir engelleyici yazarak başlayabilirsiniz :

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

Statik yöntem, menüleri mevcut bağlamdan almak için sadece bir kısaltmadır. Ardından, Menuseylem durdurucu ile karıştırılması için denetleyicinize açıklama ekleyin :

@With(Menus.class)
public class Application extends Controller {
    // …
}

Son olarak, menusdeğeri şablonlarınızdan aşağıdaki gibi alın:

@(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>

Menü yerine menüler mi demek istediniz? "örtük değer menüleri: Seq [Menü] = Menu.findAll"
Ben McCann

1
Ayrıca, projem şu anda yalnızca Java'da yazıldığı için, eylem oluşturma yoluna gidip sadece önleyicimin Scala'da yazılması, ancak tüm eylemlerimi Java'da yazılı olarak bırakmam mümkün olur mu?
Ben McCann

"menü" veya "menüler", önemli değil :), önemli olan türdür: Seq [Menü]. Cevabımı düzenledim ve bu sorunu çözmek için bir Java kalıbı ekledim.
Julien Richard-Foy

3
Son kod bloğunda arama yaparsınız @for(menu <- Menus.current()) {ancak Menushiçbir zaman tanımlanmaz (menüleri koyarsınız (küçük harf):) ctx.args.put("menus", Menu.find.all());. Bir sebebi var mı Büyük harfe dönüştüren Play gibi mi?
Cyril N.

1
@ cx42net MenusTanımlanmış bir sınıf var (Java engelleyici). @adis Evet ama onları başka bir yerde, hatta önbellekte bile saklamakta özgürsünüz.
Julien Richard-Foy

19

Bunu yapmamın yolu, navigasyon / menüm için yeni bir kontrolör oluşturmak ve onu görünümden çağırmaktır.

Böylece şunları tanımlayabilirsiniz 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>
}

Sonra ana görüşümde buna diyebilirim NavController:

@(title: String)(content: Html)
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
  </head>
  <body>
     @NavController.nav
     @content
  </body>
</html>

NavController Java'da nasıl görünmelidir? Denetleyicinin html'yi döndürmesini sağlamanın bir yolunu bulamıyorum.
Mika

Ve bu yüzden, biraz yardım istemenin hemen ardından çözümü buluyorsunuz :) Kontrolör yöntemi şöyle görünmelidir. public static play.api.templates.Html sidebar () {return (play.api.templates.Html) sidebar.render ("mesaj"); }
Mika

1
Bu, bir görünümden denetleyiciyi çağırmak için iyi bir uygulama mı? Tutkulu olmak istemiyorum, bu yüzden gerçek meraktan soruyorum.
0fnt

Ayrıca, bu şekilde isteklere dayalı bir şeyler yapamazsınız, yapabilir misiniz .., örneğin kullanıcıya özel ayarlar ..
0fnt

14

Stian'ın cevabını destekliyorum. Bu, sonuç almanın çok hızlı bir yoludur.

Java + Play1.0'dan Java + Play2.0'a yeni geçtim ve şablonlar şimdiye kadarki en zor kısım ve bir temel şablonu (başlık, başlık vb.) Uygulamanın en iyi yolu Http kullanmaktır .Context.

Etiketlerle elde edebileceğiniz çok güzel bir sözdizimi var.

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

get.scala.html nerede:

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

ve set.scala.html:

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

aşağıdakileri herhangi bir şablona yazabileceğiniz anlamına gelir

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

Bu yüzden çok okunabilir ve güzel.

Gitmeyi seçtiğim yol bu. stian - iyi tavsiye. Tüm cevapları görmek için aşağı kaydırmanın önemli olduğunu kanıtlıyor. :)

HTML değişkenlerini iletme

Henüz Html değişkenlerini nasıl geçireceğimi bulamadım.

@ (Başlık: String, içeriği: HTML)

ancak bunları blok olarak nasıl geçireceğimi biliyorum.

@ (Başlıktaki: String) (muhteva: HTML)

dolayısıyla set.scala.html'yi şu şekilde değiştirmek isteyebilirsiniz:

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

bu şekilde Html bloklarını bu şekilde geçirebilirsiniz

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

DÜZENLE: "Set" Uygulamamla Yan Etki

Yaygın bir kullanım örneği, Play'de BT şablon kalıtımı.

Bir base_template.html'niz var ve ardından base_template.html'yi genişleten page_template.html'niz var.

base_template.html şuna benzeyebilir:

<html> 
    <head>
        <title> @context.get("title")</title>
    </head>
    <body>
       @context.get("body")
    </body>
</html>

sayfa şablonu şunun gibi görünebilir:

@context.set("body){
    some page common context here.. 
    @context.get("body")
}
@base_template()

ve sonra şöyle görünen bir sayfanız var (login_page.html varsayalım)

@context.set("title"){login}
@context.set("body"){
    login stuff..
}

@page_template()

Burada dikkat edilmesi gereken önemli nokta, "vücut" u iki kez ayarlamanızdır. Bir kez "login_page.html" ve ardından "page_template.html" içinde.

Yukarıda önerdiğim gibi set.scala.html'yi uyguladığınız sürece bu bir yan etkiyi tetikliyor gibi görünüyor.

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

Sayfada iki kez "giriş şeyler ..." gösterileceği için, aynı anahtarı ikinci kez koyduğumuzda ortaya çıkan değeri döndürür. (bkz. java belgelerine imza atma).

scala, haritayı değiştirmenin daha iyi bir yolunu sağlar

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

bu yan etkiye neden olmaz.


Scala controller'da play.mvc.Htt.Context.current () 'de put metodu yok yapmaya çalışıyorum. Bir şey mi kaçırıyorum?
0fnt

argsçağrıldıktan sonra bağlamı geçerli hale getirmeyi deneyin .
guy mograbi

13

Java kullanıyorsanız ve bir engelleyici yazmak zorunda kalmadan ve @With açıklamasını kullanmak zorunda kalmadan mümkün olan en basit yolu istiyorsanız, HTTP bağlamına doğrudan şablondan da erişebilirsiniz.

Örneğin, bir şablonda bulunan bir değişkene ihtiyacınız varsa, bunu HTTP bağlamına şu şekilde ekleyebilirsiniz:

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

Daha sonra şunlarla şablondan erişebilirsiniz:

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

Açıkçası, yöntemlerinizi Http.Context.current (). Args.put ("", "") ile doldurursanız, bir durdurucu kullanmak daha iyidir, ancak basit durumlarda hile yapabilir.


Merhaba stian, lütfen cevabımdaki son düzenlememe bir göz atın. Aynı anahtarla bağımsız değişkenlere iki kez "koy" kullanırsanız, kötü bir yan etki elde edeceğinizi öğrendim. Bunun yerine ... args (anahtar) = değer kullanmalısınız.
guy mograbi

6

Stian'ın cevabına göre farklı bir yaklaşım denedim. Bu benim için çalışıyor.

JAVA KODU İLE

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

HTML ŞABLON BAŞLIĞINDA

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

VE GİBİ KULLANIN

@if(isOk) {
   <div>OK</div>
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.