प्ले 2 में हर जगह से गुजरने वाले मापदंडों से कैसे बचें?


125

प्ले 1 में, मुझे आमतौर पर कार्यों में सभी डेटा मिलते हैं, उन्हें सीधे विचारों में उपयोग करते हैं। चूंकि हमें स्पष्ट रूप से मापदंडों को स्पष्ट रूप से घोषित करने की आवश्यकता नहीं है, यह बहुत आसान है।

लेकिन प्ले 2 में, मैंने पाया कि हमें सभी मापदंडों (सहित 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>

मुझे नहीं पता कि यह अच्छा है या अनुशंसित है, क्या इसके लिए कोई बेहतर उपाय है?


शायद play2 को लिफ्ट के स्निपेट्स जैसे कुछ जोड़ना चाहिए
Freewind

जवाबों:


229

मेरी राय में, यह तथ्य कि टेम्प्लेट सांख्यिकीय रूप से टाइप किए जाते हैं, वास्तव में एक अच्छी बात है: आप गारंटी देते हैं कि आपका टेम्प्लेट कॉल करना विफल नहीं होगा यदि यह संकलित होता है।

हालांकि, यह वास्तव में कॉलिंग साइटों पर कुछ बॉयलरप्लेट जोड़ता है। लेकिन आप इसे कम कर सकते हैं (स्थैतिक टाइपिंग के फायदे खोए बिना)।

स्काला में, मैं इसे प्राप्त करने के दो तरीके देखता हूं: क्रिया रचना के माध्यम से या अंतर्निहित मापदंडों का उपयोग करके। जावा में मैं 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यदि यह इन टेम्प्लेट में एक अंतर्निहित पैरामीटर के रूप में घोषित किया गया है:

@()(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) का उपयोग करना

चूँकि जावा में स्काला का इम्पैक्टस मैकेनिज़्म या समान नहीं है, यदि आप स्पष्ट रूप से टेंपलेट्स मापदंडों से बचना चाहते हैं तो एक संभावित तरीका उन्हें उस 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>

क्या आप मेनू के बजाय मेनू का मतलब है? "अंतर्निहित वैल मेनू: Seq [मेनू] = Menu.findAll"
बेन मैककैन

1
इसके अलावा, चूंकि मेरी परियोजना अभी केवल जावा में लिखी गई है, क्या एक्शन रचना मार्ग पर जाना और स्कैला में लिखा गया मेरा इंटरसेप्टर होना संभव है, लेकिन जावा में लिखे गए मेरे सभी कार्यों को छोड़ दें?
बेन मैककेन

"मेनू" या "मेनू", इससे कोई फर्क नहीं पड़ता :), जो मायने रखता है वह है प्रकार: Seq [मेनू]। मैंने अपना उत्तर संपादित किया और इस समस्या को संभालने के लिए एक जावा पैटर्न जोड़ा।
जुलिएन रिचर्ड-फॉय

3
अंतिम कोड ब्लॉक में, आप कॉल करते हैं @for(menu <- Menus.current()) {लेकिन Menusकभी भी परिभाषित नहीं किया जाता है (आप मेनू (लोअर केस):) डालते हैं ctx.args.put("menus", Menu.find.all());। क्या कोई कारण है? प्ले की तरह जो इसे अपरकेस या कुछ में बदल देता है?
सिरिल एन।

1
@ cx42net एक Menusवर्ग परिभाषित (जावा इंटरसेप्टर) है। @adis हां, लेकिन आप उन्हें कैशे में भी दूसरी जगह स्टोर करने के लिए स्वतंत्र हैं।
जूलियन रिचर्ड-फॉय

19

जिस तरह से मैं यह करता हूं, वह सिर्फ मेरे नेविगेशन / मेनू के लिए एक नया नियंत्रक बनाना है और इसे दृश्य से कॉल करना है

तो आप अपने को परिभाषित कर सकते हैं 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>

जावा में NavController कैसे माना जाता है? मुझे html को वापस करने के लिए कंट्रोलर बनाने का कोई तरीका नहीं मिल रहा है।
मिका

और इसलिए ऐसा होता है कि आपको कुछ मदद मांगने के बाद ही समाधान मिल जाता है :) नियंत्रक विधि इस तरह दिखना चाहिए। सार्वजनिक स्थैतिक play.api.templates.Html साइडबार () {वापसी (play.api.templates.Html) साइडबार.रेंडर ("संदेश"); }
मिका

1
क्या एक दृश्य से नियंत्रक को कॉल करने के लिए यह एक अच्छा अभ्यास है? मैं एक स्टिकलर नहीं बनना चाहता, इसलिए वास्तविक जिज्ञासा से बाहर निकलना।
0fnt

इसके अलावा, आप इस तरह से अनुरोधों के आधार पर सामान नहीं कर सकते, आप कर सकते हैं .. उदाहरण के लिए उपयोगकर्ता विशिष्ट सेटिंग्स ..
0fnt

14

मैं स्टियन के जवाब का समर्थन करता हूं। यह परिणाम प्राप्त करने का एक बहुत ही त्वरित तरीका है।

मैंने अभी Java + Play1.0 से Java + Play2.0 से माइग्रेट किया है और टेम्प्लेट अब तक का सबसे कठिन हिस्सा है, और मैंने बेस टेम्पलेट (शीर्षक, सिर आदि के लिए) को लागू करने का सबसे अच्छा तरीका पाया है। Http का उपयोग करके .Context।

एक बहुत अच्छा वाक्यविन्यास है जिसे आप टैग के साथ प्राप्त कर सकते हैं।

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 वैरिएबल को कैसे पास किया जाता है।

@ (शीर्षक: स्ट्रिंग, सामग्री: एचटीएमएल)

हालाँकि, मुझे पता है कि उन्हें ब्लॉक के रूप में कैसे पारित किया जाए।

@ (शीर्षक: स्ट्रिंग) (सामग्री: एचटीएमएल)

ताकि आप 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)
}

संपादित करें: मेरे "सेट" कार्यान्वयन के साथ साइड-इफेक्ट

एक सामान्य उपयोग के मामले में यह प्ले में वंशानुक्रम टेम्पलेट है।

आपके पास एक 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" में।

ऐसा लगता है कि यह एक साइड-इफ़ेक्ट को ट्रिगर करता है, जब तक आप set.scala.html को लागू करते हैं, जैसा कि मैंने ऊपर सुझाव दिया है।

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

जैसा कि पेज "लॉगिन सामान ..." दो बार दिखाएगा क्योंकि दूसरी बार जब हम एक ही कुंजी लगाते हैं तो वह मूल्य वापस आ जाता है। (जावा डॉक्स में हस्ताक्षर देखें)।

scala मैप को संशोधित करने का एक बेहतर तरीका प्रदान करता है

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

जिसके कारण यह दुष्प्रभाव नहीं होता है।


Scala कंट्रोलर में, मैं ऐसा करने की कोशिश करता हूं कि play.mvc.Htt.Context.current () में कोई पुट विधि नहीं है। क्या मैं कुछ भूल रहा हूँ?
0fnt

argsवर्तमान कॉलिंग के संदर्भ में डालने की कोशिश करें ।
पुरुष मोगरेबी

13

यदि आप जावा का उपयोग कर रहे हैं और केवल इंटरसेप्टर लिखने के लिए और @With एनोटेशन का उपयोग किए बिना सरलतम संभव तरीका चाहते हैं, तो आप टेम्पलेट से सीधे HTTP संदर्भ भी प्राप्त कर सकते हैं।

उदाहरण के लिए, यदि आपको किसी टेम्प्लेट से उपलब्ध वैरिएबल की आवश्यकता है तो आप इसे HTTP संदर्भ में जोड़ सकते हैं

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

तब आप इसे टेम्पलेट से एक्सेस कर सकते हैं:

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

जाहिर है अगर आप अपने तरीकों को Http.Context.current ()। Args.put ("", "") से जोड़ते हैं तो आप इंटरसेप्टर का उपयोग करने के लिए बेहतर हैं, लेकिन सरल मामलों के लिए यह ट्रिक कर सकता है।


हाय स्टियन, कृपया मेरे उत्तर में मेरे अंतिम संपादन पर एक नज़र डालें। मुझे अभी पता चला है कि यदि आप एक ही कुंजी के साथ दो बार आर्गन पर "पुट" का उपयोग करते हैं, तो आपको एक बुरा साइड-इफ़ेक्ट मिलता है। आपको इसके बजाय ... args (कुंजी) = मान का उपयोग करना चाहिए।
पुरुष मोगरबी

6

स्टियन के जवाब से, मैंने एक अलग दृष्टिकोण की कोशिश की। यह मेरे लिए काम करता है।

जावा CODE में

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>
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.