Form errors i18n Play framework - forms

I'm using the form helper and a custom form template to render my forms in the Play framework like this:
#(lang: Lang)(myForm: Form[MyModel])
#import play.i18n._
#import helper._
#implicitField = #{ FieldConstructor(formTemplate.f) }
#form ( action = routes.Application.index() ){
#inputText(
field = myForm("username"),
'_label -> Messages.get(lang, "username")
)
}
When the template is called with different values for lang, the label is displayed in the respective language.
However, when the form is submitted, error messages are always displayed in the main language. (i.e. for Required fields it's always This field is required.)
As the answer to this post mentioned, I changed the default error messages like so in my language files (currently only 2):
messages.en:
username=Username
error.required=This field is required
messages.nl:
username=Gebruikersnaam
error.required=Dit veld is verplicht
How can I make sure the errors are printed in the correct language?
I've already tried doing the following in my custom template, but without success:
#(elements: helper.FieldElements)
<!-- snipped some HTML code -->
<span class="help">
#(elements.infos(elements.args.get('_lang) match {
case Some(language) => language.asInstanceOf[play.api.i18n.Lang]
case None => new Lang("en","uk")
}).mkString(", "))
</span>
And by adding '_lang -> lang to my #inputText call.
I'm used to programming in Java and have only done some Scala in the Play templates. I'm using Play 2.0.4.

I have found the easiest way of doing this (note: I program in Java) is by defining a static method in one of your models that returns the users language:
public class User{
import play.i18n.Lang;
//simplified
public static Lang getLanguage(){
if(session("language" != null){
return Lang.forCode(session.get("language"));
} else {
return Lang.forCode("en"); //default language
}
}
You can then call this static function in your Scala form template like this:
<span class="errors">#elements.errors(User.getLanguage()).mkString(", ")</span>
to display translated errors based on the default error messages in your messages.xx files.

As a general matter, if your error codes are also found in the messages.xx resource files, they get localized, even if you program a custom validator somewhere else. You don't have to have the Lang in scope or call Messages() yourself. E.g. in Scala Play:
val validPhone = """\+?[0-9_\-\. \(\)]*$""".r
val phoneCheckConstraint: Constraint[String] = Constraint("constraints.phonecheck")({
plainText =>
val errors = plainText match {
case validPhone() => Nil
case _ => Seq(ValidationError("error.phonenumber"))
}
if (errors.isEmpty) {
Valid
} else {
Invalid(errors)
}
})
If you merely have
error.phonenumber=Invalid phone number
in your messages.en file and translated versions in other messages.xx files they will get localized by Play even though no Lang was in scope at the point of declaration. So no need to pass Lang around other than in your templates and elsewhere for explicit Messages() calls.

Related

Where is the documentation to write an event handler for input text box?

Originally I wanted to know:
How do I write a handler for this?
type state = string;
type action = | ChangeName(string)
let reducer = (_state, action) => switch action { | ChangeName(text) => text }
[#react.component]
let make = () => {
let (state, dispatch) = React.usefReducer(reducer, "");
/* What is the parameter type and how do I decode it? */
let onChange = ??? => dispatch(ChangeText(????));
<input value=state onChange/>
}
Specifically, what is the parameter type for the punned onChange handler and how do I decode it?
Every reference I come across is for JS, which I'm having difficulty translating to Re.
EDIT
The answer I found by scraping github:
let onChange = event => dispatch(ChangeName(ReactEvent.Form.target(event)##value));
Say I'd like to use another JSX element, where's the documentation? OR, is their a supposition that people coming to this from elsewhere have knowledge apriori? (I'm mostly comfortable with 'c').
You can get the types of all the DOM attributes from https://github.com/rescript-lang/rescript-react/blob/v0.10.1/src/ReactDOM.res
This file contains bindings to ReScript-React's subset of DOM attributes. It has:
onChange: ReactEvent.Form.t => unit
ReactEvent.Form module is declared at https://github.com/rescript-lang/rescript-react/blob/v0.10.1/src/ReactEvent.resi#L168
When looking for anything specific to ReScript-React, search that repo.
Looks like you have the correct code to handle the event now. Btw, you have in some places the variant constructor ChangeName and in others ChangeText, I assume the correct one is one of those. The compiler will of course catch this too :-)

Generate dynamic HTML file

I am new to Scala. I have predefined html file. I want to plug dynamic content in HTML file.
e.g.
$for(products in product)
<div>product.name</div>
<div>product.description</div>
Here products is Scala collection. I can load html file and feed dynamic values(products).
Would you advise me how to do this in Scala?
Twirl templates
compiled, typesafe, composable, scala inside Html tags (akin angular js [javscript inside Html])
Twirl templates enable you to write scala inside a html, its typesafe, gets compiled to functions and are composable as well.
orders.scala.html
#(customer: Customer, orders: List[Order])
<h1>Welcome #customer.name!</h1>
<ul>
#for(order <- orders) {
<li>#order.title</li>
}
</ul>
Without any library
Scala has good support for string interpolation and also provides triple quote syntax for declaring strings with espace characters which is very handy. You can produce dynamic Html using this simple method
case class Product(name: String)
case class Html(content: String)
def produceHtml(products: List[Product]): Html = {
Html {
products.map { product =>
s"""
|<div> ${product.name} </div>
""".stripMargin
}.mkString("\n")
}
}

Play Framework 2.4.0 and I18n with Scala

I have a project with Play Framework 2.3.8 and I'm migrating in Play Framework 2.4 but I have a problem with I18n.
Now I have in a view code like this:
#Messages("components.navbar.text")(locale.MyLang)
where locale is:
object locale {
var MyLang =Lang("it")
def changeLang(newLang:String): Unit ={
MyLang=Lang(newLang)
}
}
I would mantainer this structure without using implicit lang, is possible ?
I have some situation where I use in the same page different language and in this case is difficult and boring with the implicit lang.
If I understand your question correctly, which is that you want to override the user's chosen language for certain blocks of the page, I would do this (for Play 2.4) using an implicit Messages object:
#()(implicit messages: Messages)
<!-- some section in the user's chosen language -->
<h1>#Messages("hello.world")</h1>
<!-- some section in a specific language -->
#defining(messages.copy(lang = play.api.i18n.Lang("it")) { implicit messages =>
<h2>#Messages("something.in.italian")</h2>
}
That is, use defining to create a new (implicit) messages for certain nested blocks of HTML.
If you really wanted to go to town (and I wouldn't necessarily recommend this) you could add an italian method to Messages via an implicit class:
(in my.package.utils.i18n.MessagesExtensions.scala):
package my.packages.utils.i18n
import play.api.i18n.{Lang, Messages}
implicit class MessagesExtensions(messages: Messages) {
def italian = messages.copy(lang = Lang("it"))
// and an `as` method for good measure:
def as(code: String) = messages.copy(lang = Lang(code))
}
To have that work in a view you need to add the class to your templateImport in your build.sbt:
templateImports in Compile ++= Seq(
"my.packages.utils.i18n.MessagesExtensions"
)
Then in your templates you can just to this:
#()(implicit messages: Messages)
<!-- some section in the user's chosen language -->
<h1>#Messages("hello.world")</h1>
<!-- some section in a specific language -->
#defining(messages.italian) { implicit messages =>
<h2>#Messages("something.in.italian")</h2>
....
}
<!-- or singly, in another language -->
<h3>#Messages("another.thing.in.french")(messages.as("fr"))</h3>
But that might be overkill, unless it really saves you a lot of boilerplate language switching.

Play framework dynamic template include

I have a template that has to include another template based on the file name that comes from the database. For example, here is a template that takes a String that contains the name of the template file that will be included in another template.
#(sourceCodeFileName: Option[String])
#{sourceCodeFileName match {
case Some(sourceCode) => {
#sourcecode.sourceCodeFileName + "scala.html"
}
}}
Where sourcecode is the package where the actual template resides. For example., if the String parameter to the above template is given as myview, then I want to include myview.scala.html. Is there a way to do this in Play framework?
To inject HTML from a static file to a scala template, you can define a function in your template:
#import scala.io.Source
#injectHtmlFromFile(sourceCodeFilename: String) = { #{
Html(Source.fromFile("static/html/" + sourceCodeFilename + ".html").mkString)
}}
…and call it later in the template this way:
#injectHtmlFromFile(sourceCode.sourceCodeFileName)
Side note
I'm not sure I quite understand the question – I've answered the OP's comment and the same question posted by him on Google Groups.
A Play scala template is a function returning Html. You should obtain the template object in the controller and pass it to the template. But in simple cases it's just easier to pass the rendered Html:
Your template would then look like this:
#(content: Option[Html])
#content
and the controller:
object Application extends Controller {
def index = Action {
val sourceCodeFileName= ...
Ok(Some(Class.forName("views.html."+sourceCodeFileName)
.getConstructor().newInstance().asInstanceOf[() => play.api.templates.Html]()
))
}
}

Cannot access the parameter of a Menu.param from a Lift Snippet

I'm trying to extract the parameter from a Lift Menu.param within a snippet so that I can use it to create a named Comet. However, I get a NullPointerException when I try to pass the parameter to the snippet using SnippetDisptach in my Boot.scala, as suggested here:
http://comments.gmane.org/gmane.comp.web.lift/44299
I've created the Menu item as follows:
object AnItemPage {
// create a parameterized page
def menu = Menu.param[Item]("Item", "Item",
s => fetchItem(s), item => item._id.toString) / "item"
private def fetchItem(s:String) : Box[Item] = synchronized {
ItemDAO.findById(ObjectId.massageToObjectId(s))
}
}
I've added the menu to SiteMap. I've also created a Snippet which I would like to pick up the Item parameter. (I'm using fmpwizard's InsertNamedComet library here):
class AddCometItemPage(boxedItem: Box[Item]) extends InsertNamedComet with DispatchSnippet{
val item : Item = boxedItem.openOr(null)
override lazy val name= "comet_item_" + item._id.toString
override lazy val cometClass= "UserItemCometActor"
def dispatch = null
}
My next step is to crate an instance of this class as demonstrated by David Pollak here:
http://comments.gmane.org/gmane.comp.web.lift/44299
This is what I have added to my Boot.scala:
LiftRules.snippetDispatch.append {
case "item_page" => new AddCometItemPage(AnItemPage.menu.currentValue)
}
My item.html references this snippet:
<div class="lift:item_page">
I get the following null pointer exception when I compile and run this:
Exception occurred while processing /item/5114eb4044ae953cf863b786
Message: java.lang.NullPointerException
net.liftweb.sitemap.Loc$class.siteMap(Loc.scala:147)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.siteMap(Menu.scala:170)
net.liftweb.sitemap.Loc$class.allParams(Loc.scala:123)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.allParams(Menu.scala:170)
net.liftweb.sitemap.Loc$class.net$liftweb$sitemap$Loc$$staticValue(Loc.scala:87)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.net$liftweb$sitemap$Loc$$staticValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.paramValue(Loc.scala:85)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.paramValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.currentValue(Loc.scala:114)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.currentValue(Menu.scala:170)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:107)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:106)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.common.Full.map(Box.scala:553)
net.liftweb.util.NamedPF$.applyBox(NamedPartialFunction.scala:97)
net.liftweb.http.LiftRules.snippet(LiftRules.scala:711)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.http.LiftSession.net$liftweb$http$LiftSession$$findSnippetInstance(LiftSession.scala:1505)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1670)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1669)
Has anybody any idea where I'm going wrong? I've not been able to find a lot of information on Menu.param.
Thank you very much for your help.
f
I have never tried what you are doing, so I am not sure the best way to accomplish it. The way you are using the Loc Param, you are extracting a variable from a URL pattern. In your case, http://server/item/ITEMID where ITEMID is the string representation of an Item, and which is the value that gets passed to the fetchItem function. The function call will not have a value if you just arbitrarily call it, and from what I can see you are requesting a value that is not initialized.
I would think there are two possible solutions. The first would be to use S.location instead of AnItemPage.menu.currentValue. It will return a Box[Loc[Any]] representing the Loc that is currently being accessed (with the parameters set). You can use that Loc to retrive currentValue and set your parameter.
The other option would be to instantiate the actor in your snippet. Something like this:
item.html
<div data-lift="AnItemPage">
<div id="mycomet"></div>
</div>
And then in your AnItemPage snippet, something like this:
class AnItemPage(item: Item) {
def render = "#mycomet" #> new AddCometItemPage(item).render
}
I haven't tested either of those, so they'll probably need some tweaking. Hopefully it will give you a general idea.