Working aroung passing an Implicit parameter to every single template - scala

I'm doing some work with PlayFramework templates, but I have encountered a problem. We're using play's helpers which requires Messages (imported from play.api.i18n). Everything was ok until our Designer wanted to have login form in form of Modal... Because it'll be appended to every single template, we'll need to add that messages parameter everywhere - which is ugly IMHO.
Is there a way to work that around? Passing it everywhere would mean that I have to Inject() it everywhere, even if it's needed only to be passed to shut the typechecker.
Sample Page:
#(project: model.Project)(implicit request: Request[AnyContent], messages: Messages)
#main(project.name){
<h1>#project.name</h1>
<ul>
#for(member <- project.members) {
<li>#member</li>
}
</ul>
}{}
Fragment of Main template:
#(title: String)(content: Html)(additionalImport: Any)(implicit req: Request[AnyContent], messages: Messages)
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
#* this call actually needs that param. *#
#header.navbar()
<div class="container">
#req.flash.get("error").map { error =>
<div class="flash-error">#error</div>
}
#content
</div>
</body>
</html>
The Form:
#import model.UserLoginData
#(loginForm: Form[UserLoginData])(implicit req: Request[AnyContent], messages: Messages)
#helper.form(action = routes.AuthenticationController.login()) {
#loginForm.globalErrors.map { error =>
<div class="error">#error.message</div>
}
#helper.inputText(loginForm("login"))
#helper.inputPassword(loginForm("password"))
<input type="submit" value="Zaloguj"/>
}
Zapomniałem hasła

Here I see two work arounds. Unfortunately, I am not able to test them now, but I believe they will both work.
Get rid of the messages parameter from the form template. Use Play.current.injector.instanceOf[MessagesApi] to get MessagesApi implementation just inside the template (here is a question about accessing injector without an #Inject annotation). Then you may call the method preferred(Http.RequestHeader request):Messages to get a Messages instance, and then you need to explicitly pass this to a helper method.
If you just want to get rid of injection and you don't mind passing an implicit messages parameter to every single template, you may implement your own version of the I18nSupport trait. Here I mean that you usually write the controller in the following way:
class SomeController #Inject()(val messagesApi: MessagesApi) extends Controller with I18nSupport. The messagesApi val overrides the same value of the I18nSupport trait. You may extend this trait with your own MyI18Support trait, and inject MessagesApi inside it (UPD: you may either #Iinject or use Play.current.injector). Then you will only need to write the controller as follows: class SomeController extends Controller with MyI18nSupport.

Related

How to access controller variable without passing to Play html

Is it possible to access Play Framework scala contoller variables wihout passing to scala.html template?
For example my controller code as below,
def index = Action { request =>
val orgId = '12132132'
Ok(views.html.index(request))
}
My index.scala.html as below,
#(implicit request: RequestHeader)
#main("Test") {
I want to access controller "orgId" variable here wihtout passing here.
}
Here is my main.scala.html,
#(title: String)(content: Html)
<html lang="en">
<head>
<title>#title</title>
</head>
<body>
#content <!-- index.html placed here -->
</body>
<div>
Here I have bootstrap side menu and I want to display controller variable here without passing to main.scala.html templete.
</div>
</html>
Thanks.
You cannot do it since it private for the function. No other function can access it (even not in the same class).
If you are using object as instance of the controller (instead of class) and you use value of the object itself then you can do it, but It HIGHLY NOT recommended.
e.g.
object MyController extends controller {
val orgId = '12132132'
def index = Action { request =>
Ok(views.html.index(request))
}
...
}
#(implicit request: RequestHeader)
#main("Test") {
I want to access controller "#{MyController.orgId}" variable here without passing here.
}
If you need to pass some data that isn't depend on the request, meaning it can be value of the object, then it also may be outside the controller. So you can create an object that will hold those values, and access them.
Technically it the same as use the controller, but logically it better separation.

Binding an html form action to a controller method that takes some parameters

In my Find controller I have a method like:
public Result findLatest(String repoStr) {
............
}
Which is linked through a route:
GET /latest controllers.Find.findLatest(repo: String)
Then, I have a form in a view like:
<form action="#routes.Find.findLatest()" method="get">
....
<select name="repo">....</select>
</form>
But obviously that is failing, because it is expecting some parameters that I do not fulfill in the action. What is the correct way to do this without having to end up leaving the findLatest method taking no parameters in my controller?
You could change the routes to accept an empty string:
GET /latest/:repo controllers.Find.findLatest(repo: String = "")
Then configure your controller function to handle empty string.
That way,
<form action="#routes.Find.findLatest()" method="get">
....
<select name="repo">....</select>
will evaluate repo as an empty string at the controller level.
Edit: Support for this implementation was dropped in Play v 2.1
You may be interested in Play's Optional parameters e.g. play.libs.F.Option[String]
Example: How to handle optional query parameters in Play framework
GET /latest/:repo/:artifact controllers.Find.findLatestArtifact(repo: play.libs.F.Option[String], artifact: play.libs.F.Option[String])
This will allow you flexibility in which arguments need to be provided.
Not sure which language you're using but the link above contains an example for scala and the method declaration in java would look something like:
import play.libs.F.Option;
public static Result findLatestArtifact(Option<String> repo, Option<String> artifact){ ... }
and updated implementation 2.1
Routes with optional parameter - Play 2.1 Scala
EDIT: play 2.1+ Support : Props to #RobertUdah below
Initializing to null:
GET /latest/ controllers.Find.findLatest(repo: String = null)
GET /latest/:repo controllers.Find.findLatest(repo: String)
<form action="#routes.Find.findLatest()" method="get">
Normally all form data go in the body and you can retrieve them in your action method with bindFromRequest() (see docs).
If you really want to pass one form element as a part of the URL then you have to dynamically compose your URL in JavaScript and change your route.
Your route could look like:
GET /latest/:repo controllers.Find.findLatest(repo: String)
And the JavaScript part like (I didn't actually test the code):
<form name="myform" action="javascript:composeUrl();" method="get">
....
<select name="repo">....</select>
</form>
<script>
function submitform() {
var formElement = document.getElementsByName("myform");
var repo = formElement.options[e.selectedIndex].text;
formElement.action = "/lastest/" + repo;
formElement.submit();
}
</script>
Cavice suggested something close to what I consider the best solution for this (since F.Option are not supported anymore with the default binders in Play 2.1 ).
I ended up leaving the route like:
GET /latest controllers.Find.findLatest(repo=null)
and the view like:
<form action="#routes.Find.findLatest(null)" method="get">
<select name="repo"> .... </select>
....
</form>
and in the controller:
public Result findLatest(String repoStr) {
if(repoStr==null) {
repoStr=Form.form().bindFromRequest().get("repo");
.....
This allows me to have a second route like:
GET /latest/:repo controllers.Find.findLatest(repo: String)

missing arguments for method apply... Play Framework 2.4 compilation error

Compilation error: missing arguments for method apply in class newPost;
follow this method with `_' if you want to treat it as a partially applied function
I don't understand how template handling methods have to look like and what complier require of me.
https://github.com/flatlizard/blog
controller:
def addPost = Action{ implicit request =>
Ok(views.html.newPost(postForm))
}
def createPost = Action { implicit request =>
postForm.bindFromRequest.fold(
hasErrors => BadRequest,
success => {
Post.create(Post(new Date, success.title, success.content))
Ok(views.html.archive("my blog", Post.all))
})
}
routes:
GET /archive/new controllers.Application.addPost
POST /archive controllers.Application.createPost
view:
#(postForm: Form[Post])(content: Html)(implicit messages: Messages)
#import helper._
#form(routes.Application.createPost) {
#inputDate(postForm("date"))
#textarea(postForm("title"))
#textarea(postForm("content"))
<button id="submit" type="submit" value="Submit" class="btn btn-primary">Submit</button>
}
UPDATE
I solved problem adding the following imports in controller file:
import play.api.i18n.Messages.Implicits._
import play.api.Play.current
See Play 2.4 migration:
https://www.playframework.com/documentation/2.4.x/Migration24#I18n
Compilation error
Your view expects three parameters to be passed, but you are passing only one. To solve your compilation error, change the signature in your view from#(postForm: Form[Post])(content: Html)(implicit messages: Messages) to #(postForm: Form[Post])(implicit messages: Messages).
Parameters and views
The second parameter in your example (content: Html) is used when you combine several views:
index.scala.html
#(text: String)
#main("Fancy title") {
<div>#text</div>
}
main.scala.html
#(title: String)(content: Html)
<html>
<head>
<title>#title</title>
</head>
<body>
#content
<body>
</html>
Call in controller
Ok(views.html.index("Some text"))
In this example you are passing "Some text" to index view. Index view then calls main view passing another two parameters. title and content, where content is the html in between the curly braces of index.scala.html (<div>#text</div>)
Finally implicit parameters: (implicit messages: Messages) must be somewhere in scope to be passed implicitly to your view. For example like this in your controller:
def addPost = Action{ implicit request =>
implicit val messages: Messages = ...
Ok(views.html.newPost(postForm))
}
I solved problem adding the following imports in controller file:
import play.api.i18n.Messages.Implicits._
import play.api.Play.current
See Play 2.4 migration: https://www.playframework.com/documentation/2.4.x/Migration24#I18n
Update
Actually this is a bad way cause here used Play.current which will become deprecated soon. Here another solution using dependency injection:
routes:
GET /archive/new #controllers.Application.addPost
POST /archive #controllers.Application.createPost
controller:
class Application #Inject() (val messagesApi: MessagesApi)
extends Controller with I18nSupport{
...
}
https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection

Play Framework - Getting Session Data in Views

How can I get session data in views in Play Framework 2.3.x?
I used to use session.get("sessionName") but now I get not found: value session when I try to use that.
You ought to pass all mandatory variables to a view. In order to get access to the session you should declare it as parameter of your template.
#(title:String)(implicit session: Session)
<html>
<head>
<title>#title</title>
</head>
<body>
#session.get("sessionname")
</body>
</html>
Using implicit will allow you to avoid passing the session explicitly from a controller.
def index = Action { implicit req =>
Ok(views.html.index("Page Title"))
}

Lift 2.6: How to append a NodeSeq rather than using SetHtml()

I use SetHtml() all the time to swap chunks of HTML as part of the return trip of an ajax call, ie. when my "Edit Item" ajax call returns, I swap the contents of with form elements representing the "Item" to be edited.
Now I'm trying to add a new item to a list (so that "Item 2" appears as the next li under "Item 1"). My html looks like (this is a greatly simplified version) this:
<div data-lift="form.ajax">
<div data-lift="JsCmdTest">
<test:submitbutton></test:submitbutton>
<ul id="targetdiv">
<li>Item 1</li>
</ul>
</div>
</div>
and my Lift code looks like this
class JsCmdTest extends StatefulSnippet
{
def dispatch = { case "render" => render }
def bindStuff(ns: NodeSeq): NodeSeq =
{
bind("test", ns,
"submitbutton" -> SHtml.ajaxButton("Go", ()=> {
val newLi = Jx(<div>Item 2</div>)
(ElemById("targetdiv") ~> JsFunc("appendChild", newLi.toJs)).cmd
})
)
}
def render: (NodeSeq)=>NodeSeq = (ns: NodeSeq) => {
bindStuff(ns)
}
}
When I run this, the ajax call is fired, and the response looks fine, but I get the following error in the browser console:
The server call succeeded, but the returned Javascript contains an error: NotFoundError: Failed to execute 'appendChild' on 'Node': The new child element is null.
WANTED: a way to append replacement HTML to an existing < ul >. I feel like the way I'm going about appending seems way more esoteric than probably needed, but i haven't found any other way to do it.
Thanks in advance!
you're using a very old syntax for binding. If I am not mistaken, a new way for bindings was introduced somewhere in lift-2, and it is the recommended one since somewhere in lift-2.2. (I'll write the syntax below.)
JsFunc is an anonymous function, like def local(a: A): B. You don't need to send anonymous function, you can send the code directly. (See below.)
So, I recommend something like this:
import net.liftweb.http.js.{JsExp, JsCmd, JsCmds}
import net.liftweb.http.js.jquery.JqJE.{JqAppend, JqId}
def render = {
val appendJs: JsExp = JqId("targetdiv") ~> JqAppend(newLi)
"#mySubmitButton" #> SHtml.ajaxButton("Go", () => appendJs.cmd)
}
You'll also have to adapt the HTML a little, like using a normal <button> with id="mySubmitButton".