كيفية تجنب تمرير المعلمات في كل مكان في 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>

لا أعرف إذا كان هذا جيدًا أم موصى به، فهل هناك حل أفضل لذلك؟

هل كانت مفيدة؟

المحلول

في رأيي، حقيقة أن القوالب مكتوبة بشكل ثابت هي في الواقع جيد شيء:أنت متأكد من أن استدعاء القالب الخاص بك لن يفشل إذا تم تجميعه.

ومع ذلك، فإنه يضيف بالفعل بعض النموذج النموذجي إلى مواقع الاتصال.لكن يمكنك تقليله (دون فقدان مزايا الكتابة الثابتة).

في Scala، أرى طريقتين لتحقيق ذلك:من خلال تكوين الإجراءات أو باستخدام المعلمات الضمنية.في Java أقترح استخدام Http.Context.args Map لتخزين القيم المفيدة واسترجاعها من القوالب دون الحاجة إلى تمريرها بشكل صريح كمعلمات قوالب.

باستخدام المعلمات الضمنية

ضع ال 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 template بواسطة مترجم 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 لا تحتوي على آلية 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>

أنا أؤيد إجابة ستيان.هذه طريقة سريعة جدًا للحصول على النتائج.

لقد انتقلت للتو من 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)
}

يحرر:التأثير الجانبي مع تنفيذ "Set" الخاص بي

حالة الاستخدام الشائعة هي وراثة القالب في Play.

لديك 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()

ثم لديك صفحة (لنفترض تسجيل الدخول_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}

والذي لا يسبب هذا الأثر الجانبي.

إذا كنت تستخدم 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(""،") فمن الأفضل استخدام أداة اعتراضية، ولكن في الحالات البسيطة قد تؤدي المهمة.

من إجابة ستيان، حاولت اتباع نهج مختلف.هذا يعمل بالنسبة لي.

في كود جافا

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