Comment éviter de passer des parameters partout dans play2?

Dans play1, je récupère généralement toutes les données en actions, les utilise directement dans les vues. Comme nous n’avons pas besoin de déclarer explicitement les parameters en vue, cela est très facile.

Mais dans play2, j’ai trouvé que nous devions déclarer tous les parameters (y compris la request ) dans la tête des vues, il serait très ennuyeux d’obtenir toutes les données dans les actions et de les transmettre aux vues.

Par exemple, si je dois afficher les menus chargés depuis la firebase database sur la page d’accueil, je dois les définir dans main.scala.html :

 @(title: Ssortingng, menus: Seq[Menu])(content: Html) @title  
@for(menu<-menus) { @menu.name }
@content

Ensuite, je dois le déclarer dans chaque sous-page:

 @(menus: Seq[Menu]) @main("SubPage", menus) { ... } 

Ensuite, je dois récupérer les menus et les afficher dans toutes les actions:

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

Pour l’instant ce n’est qu’un paramètre dans main.scala.html , et si il y en avait beaucoup?

Donc, enfin, j’ai décidé de Menu.findAll() directement Menu.findAll() :

 @(title: Ssortingng)(content: Html) @title  
@for(menu<-Menu.findAll()) { @menu.name }
@content

Je ne sais pas si c’est bon ou recommandé, y a-t-il une meilleure solution pour cela?

    À mon avis, le fait que les modèles soient statiquement typés est en fait une bonne chose: vous êtes assuré que l’appel de votre modèle n’échouera pas s’il est compilé.

    Cependant, il ajoute en effet un peu de matière aux sites d’appel. Mais vous pouvez le réduire (sans perdre les avantages de typage statique).

    En Scala, je vois deux moyens d’y parvenir: la composition des actions ou l’utilisation de parameters implicites. En Java, je suggère d’utiliser la carte Http.Context.args pour stocker des valeurs utiles et les récupérer à partir des modèles sans avoir à passer explicitement comme parameters de modèles.

    Utiliser des parameters implicites

    Placez le paramètre menus à la fin de vos parameters de modèle main.scala.html et marquez-le comme «implicite»:

     @(title: Ssortingng)(content: Html)(implicit menus: Seq[Menu])  @title  
    @for(menu<-menus) { @menu.name }
    @content

    Maintenant, si vous avez des modèles appelant ce modèle principal, vous pouvez avoir le paramètre menus implicitement transmis au modèle main par le compilateur Scala s’il est également déclaré comme paramètre implicite dans ces modèles:

     @()(implicit menus: Seq[Menu]) @main("SubPage") { ... } 

    Toutefois, si vous souhaitez le faire implicitement passer de votre contrôleur, vous devez le fournir en tant que valeur implicite, disponible dans la scope à partir de laquelle vous appelez le modèle. Par exemple, vous pouvez déclarer la méthode suivante dans votre contrôleur:

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

    Ensuite, dans vos actions, vous pourrez écrire ce qui suit:

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

    Vous pouvez trouver plus d’informations sur cette approche dans cet article de blog et dans cet exemple de code .

    Mise à jour : Un bel article de blog démontrant ce modèle a également été écrit ici .

    Utilisation de la composition d’actions

    En fait, il est souvent utile de transmettre la valeur RequestHeader aux modèles (voir par exemple cet exemple ). Cela n’ajoute pas beaucoup de temps à votre code de contrôleur car vous pouvez facilement écrire des actions recevant une valeur de requête implicite:

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

    Donc, comme les modèles reçoivent souvent au moins ce paramètre implicite, vous pouvez le remplacer par une valeur plus riche contenant par exemple vos menus. Vous pouvez le faire en utilisant le mécanisme de composition des actions de Play 2.

    Pour ce faire, vous devez définir votre classe Context encapsulant une requête sous-jacente:

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

    Ensuite, vous pouvez définir la méthode ActionWithMenu suivante:

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

    Qui peut être utilisé comme ceci:

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

    Et vous pouvez prendre le contexte comme paramètre implicite dans vos modèles. Par exemple pour main.scala.html :

     @(title: Ssortingng)(content: Html)(implicit context: Context) @title  
    @for(menu <- context.menus) { @menu.name }
    @content

    L’utilisation de la composition d’actions vous permet d’agréger toutes les valeurs implicites requirejses par vos modèles en une seule valeur, mais vous pouvez également perdre de la flexibilité…

    Utiliser Http.Context (Java)

    Comme Java n’a pas de mécanisme implicite de Scala ou similaire, si vous voulez éviter de passer explicitement des parameters de modèles, il est possible de les stocker dans l’object Http.Context qui n’existe que pendant la durée d’une requête. Cet object contient une valeur args de type Map .

    Ainsi, vous pouvez commencer par écrire un intercepteur, comme expliqué dans la documentation :

     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 current() { return (List)Http.Context.current().args.get("menus"); } } 

    La méthode statique est juste un raccourci pour récupérer les menus du contexte actuel. Ensuite, annotez votre contrôleur à mélanger avec l’intercepteur d’action Menus :

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

    Enfin, récupérez la valeur des menus de vos modèles comme suit:

     @(title: Ssortingng)(content: Html)  @title  
    @for(menu <- Menus.current()) { @menu.name }
    @content

    La façon dont je le fais est de créer un nouveau contrôleur pour mon menu de navigation et de l’appeler depuis la vue.

    Vous pouvez donc définir votre NavController :

     object NavController extends Controller { private val navList = "Home" :: "About" :: "Contact" :: Nil def nav = views.html.nav(navList) } 

    nav.scala.html

     @(navLinks: Seq[Ssortingng]) @for(nav <- navLinks) { @nav } 

    Puis, dans ma vue principale, je peux appeler ce NavController :

     @(title: Ssortingng)(content: Html)    @title   @NavController.nav @content   

    Si vous utilisez Java et que vous souhaitez simplement utiliser la méthode la plus simple possible sans avoir à écrire un intercepteur et à l’aide de l’annotation @With, vous pouvez également accéder au contexte HTTP directement à partir du modèle.

    Par exemple, si vous avez besoin d’une variable disponible à partir d’un modèle, vous pouvez l’append au contexte HTTP avec:

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

    Vous pouvez ensuite y accéder depuis le modèle avec:

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

    Évidemment, si vous utilisez vos méthodes avec Http.Context.current (). Args.put (“”, “”), vous feriez mieux d’utiliser un intercepteur, mais pour des cas simples, il pourrait vous aider.

    Je soutiens la réponse de stian. C’est un moyen très rapide d’obtenir des résultats.

    Je viens de migrer de Java + Play1.0 vers Java + Play2.0 et les templates sont la partie la plus difficile jusqu’ici, et la meilleure façon de mettre en œuvre un template de base (pour le titre, la tête, etc.) est d’utiliser le Http .Le contexte.

    Il y a une très bonne syntaxe que vous pouvez obtenir avec les tags.

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

    où get.scala.html est:

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

    et set.scala.html est:

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

    signifie que vous pouvez écrire ce qui suit dans n’importe quel modèle

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

    Donc c’est très lisible et gentil.

    C’est comme ça que j’ai choisi d’aller. stian – bons conseils. Prouve qu’il est important de faire défiler la liste pour voir toutes les réponses. 🙂

    Passage des variables HTML

    Je n’ai pas encore compris comment passer des variables HTML.

    @ (title: Ssortingng, content: Html)

    Cependant, je sais comment les transmettre en bloc.

    @ (title: Ssortingng) (contenu: HTML)

    vous pouvez donc vouloir remplacer set.scala.html par

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

    de cette façon, vous pouvez passer des blocs HTML comme ça

     @context.set("head"){  @callSomeFun(withParameter) } 

    EDIT: Effet secondaire avec mon implémentation “Set”

    Un cas d’utilisation commun, il modèle l’inheritance dans Play.

    Vous avez un base_template.html et vous avez ensuite page_template.html qui étend base_template.html.

    base_template.html pourrait ressembler à quelque chose

        @context.get("title")   @context.get("body")   

    tandis que le modèle de page peut ressembler à quelque chose comme

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

    et puis vous avez une page (supposons que login_page.html) qui ressemble à

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

    La chose importante à noter ici est que vous définissez “corps” deux fois. Une fois dans “login_page.html” puis dans “page_template.html”.

    Il semble que cela déclenche un effet secondaire, tant que vous implémentez set.scala.html comme je l’ai suggéré ci-dessus.

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

    comme la page afficherait “login stuff …” deux fois parce que put retourne la valeur qui apparaît la deuxième fois que nous mettons la même clé. (voir la signature en java docs).

    scala fournit un meilleur moyen de modifier la carte

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

    ce qui ne provoque pas cet effet secondaire.

    De la réponse de Stian, j’ai essayé une approche différente. Cela fonctionne pour moi.

    EN JAVA CODE

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

    EN HTML TEMPLATE HEAD

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

    ET UTILISATION COMME

     @if(isOk) { 
    OK
    }