How Do I Call Snippets From Different Packages In Lift? - scala

I am trying to call one of 2 snippet methods with the same name and same class, but these snippets are located in different packages. Here's the example code:
Snippet 1:
package v1.site.snippet
class TestSnippet {
def test = { println("printed from v1") }
}
Snippet 2:
package v2.site.snippet
class TestSnippet {
def test = { println("printed from v2") }
}
index.html:
<div class="lift:TestSnippet.test"></div>
So how do I tell index.html which TestSnippet.test to call? Both packages have been added in my Boot.scala.

One option:
LiftRules.snippetDispatch.append {
case "V1TestSnippet" => new v1.site.snippet.TestSnippet
case "V2TestSnippet" => new v2.site.snippet.TestSnippet
}
Your snippets must then inherit DispatchSnippet and define def dispatch = { case "test" => test _ } etc. You then invoke the snippets from the template as V1TestSnippet or V2TestSnippet.
Alternatively, something like
LiftRules.snippets.append {
case "V1TestSnippet"::"test"::Nil => (new v1.site.snippet.TestSnippet).test _
case "V2TestSnippet"::"test"::Nil => (new v2.site.snippet.TestSnippet).test _
}
I believe the List is the snippet name in the template split on dots.

Related

how to setup a for loop inside if statement with correct syntax Play framework Scala Template

I am trying to setup a variable in the scala template. Loop through the roles that user have , if found out the user is customer , then do something with the input. If not then do something else.
But scala isnt that simple , it won't compile on following code.
#var = #{ if(user != null){
#for(role <- user.roles.filter(_.getName()=="customer")) {
var=#customer(input)
}
}
}
#if( var == null){
var=#others(input)
}
It gives me two errors
t.scala.html:275:: identifier expected but 'for' found.
[error] #for(role <- user.roles.filter(_.getName()=="customer"))
t.scala.html:278: expected start of definition
Also , is there a better way to do this in scala ? Thanks
My reference : Scala template set variable
Update:
My goal was trying to do something like below , but in scala template:
result=null
for role in User.roles:
if(role == "customer"):
result=customer(xyz)
break
if(result==null):
result = others(xyz)
To set up a for loop inside of an if statement in a Scala template, you don't need to assign a variable. You can simply use an if block in the template where you want to display stuff. For example
#if(user != null) {
#for(role <- user.roles.filter(_.getName()=="customer")) {
#customer(input)
#* Do other stuff related to 'role' and 'input' here *#
}
} else {
#* Do something else *#
}
For further reference I encourage you to look at the documentation for Play templates. If you really want to define a variable you could do it using the defining helper:
#defining(user.getFirstName() + " " + user.getLastName()) { fullName =>
<div>Hello #fullName</div>
}
Instead of defining a variable you could also define a resusable block, which might be useful in your case. For example,
#customer_loop(input: String) = {
#if(user != null) {
#for(role <- user.roles.filter(_.getName()=="customer")) {
#customer(input)
#* Do other stuff related to 'role' and 'input' here *#
}
} else {
#* Do something else *#
}
}
To declare a variable do
#import scala.Any; var result:Any=null //where Any is the datatype accoding to your requirement
To reassign its value do
#{result = "somevalue"}
So the solution accoding to the pseudo you provided
#import java.lang.String; var result:String=null
#import scala.util.control._;val loop = new Breaks;
#loop.breakable {
#for(role <- roleList) {
#if(role.equals("customer")) {
#{
result = "somevalue"
}
#{loop.break};
}
}
}
#if(result==null){
#{result="notfound"}
}
Also check Similar1,Similar2

Playframework 2.2.1 Scala - Form bindFromRequest error

I'm trying to register a tuple to a DB and upload the image to Amazon S3.
I divided the code to two parts : 1) Working Code and 2) Non Working Code.
Working code means It works as expected. The image file is uploaded sucessfully to S3.
Non working code is always folding to index_error page which I'm trying to solve but I can't figure out where I'm missing.
Thank you for help!
Working Code
def index = Action {
Ok(views.html.upload_test_index("File Upload In Play"))
}
def uploadFile = Action(parse.multipartFormData) { request =>
request.body.file("fileUpload").map { video =>
val newFile = File.createTempFile("temp-uploaded-", video.filename)
video.ref.moveTo(newFile, true)
new S3Sender(newFile, video.filename).send
}.getOrElse {
Redirect(routes.Application.index)
}
Ok("File has been uploaded")
}
Non Working Code - Controller
val anuncioForm = Form(
tuple(
"label" -> nonEmptyText,
"imgName" -> text
)
)
def createAnuncio = Action(parse.multipartFormData) { implicit request =>
anuncioForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.index_error(formWithErrors)),
{
case (label, imgName) =>
request.body.file("imgName").map { imgName =>
val newFile = File.createTempFile("temp-uploaded-", imgName.filename)
val fileName = imgName.filename
imgName.ref.moveTo(newFile, true)
new S3Sender(newFile, fileName).send
Anuncio.create(label, fileName)
}
println("criou")
Redirect(routes.Application.anuncios)
}
)
}
def anuncios = Action {
Ok(views.html.index(Anuncio.all(), anuncioForm))
}
def newAnuncio = Action {
Ok(views.html.create(anuncioForm))
}
Non Working Code - Template
#(anuncioForm: Form[(String, String)])
#import helper._
#main("Criar um novo anuncio") {
#form(action = routes.Application.createAnuncio, 'enctype -> "multipart/form-data") {
#inputText(anuncioForm("label"))
#*<input type="file" name="imgName">*#
#inputFile(anuncioForm("imgName"))
<input type="submit" value="Create">
}
}
Edited
I used the code below to know the error message.
formWithErrors => BadRequest(views.html.index_error(formWithErrors.errorsAsJson))
Surprisingly I got the message below. I can't figure out why this message?!
{"imgName":["This field is required"]}
Do you need to have the file contents as a part of the form itself? You declare the imgName as a text in the form in your controller, but you use it as a input type="file" in your view, which I would imagine is contradictory.
A few things you could take a look at:
how to handle fileupload into a table in play framework? which talks about separating the file from the form as well as an (untested?) single submit for both a form and a file, and possible some other clues
How to include a picture type in a form in Play!2 in Scala? which is doing something similar to what you are doing
http://www.playframework.com/documentation/2.1.2/ScalaFileUpload

Typesafe config: How to iterate over configuration items

In my Play application I've a configuration like this:
social {
twitter {
url="https://twitter.com"
logo="images/twitter.png"
}
facebook {
url="https://www.facebook.com"
logo="images/facebook.png"
}
}
Ho do I iterate over all the social entries to get url and logo for each entry?
<table border="0" cellspacing="0" cellpadding="2"><tr>
#configuration.getConfig("social").map { config =>
#for(item <- config.entrySet) {
<td><a href="item.getString("url")">
<img src="#routes.Assets.at("item.getString("logo")").absoluteURL()" width="24" height="24"/></a></td>
}
}
</table>
Of course, item.getString in the snippet here above does not work... it just shows what I'm trying to achieve.
The final objective would be to be able to add any further social url without having to modify the page template.
If you change the config to:
"social" : [
{
name="twitter",
url="https://twitter.com",
logo="images/twitter.png"
},
{
name="facebook",
url="https://www.facebook.com",
logo="images/facebook.png"
}
]
You could do it like this:
#(message: String)(implicit request: RequestHeader)
#import play.api.Play.current
<table border="0" cellspacing="0" cellpadding="2"><tr>
#current.configuration.getConfigList("social").get.map { config =>
<td><a href="#config.getString("url")">
<img src="#routes.Assets.at(config.getString("logo").get).absoluteURL()" width="24" height="24"/></a></td>
}
</table>
For posterity, here's another way to iterate over a nested config like you had. I prefer that format to the array one and I'd rather make my config cleaner than the code.
import collection.JavaConversions._
val socialConfig = ConfigFactory.load.getConfig("social")
socialConfig.root.map { case (name: String, configObject: ConfigObject) =>
val config = configObject.toConfig
println(config.getString("url"))
println(config.getString("logo"))
}
I'm sure the OP could convert this into a Twirl template. That's about as clean as I can get it.
In case you're using Java, for a config like:
"social" : [
{
name="twitter",
url="https://twitter.com",
logo="images/twitter.png"
},
{
name="facebook",
url="https://www.facebook.com",
logo="images/facebook.png"
}
]
this will work:
ConfigList socials = ConfigFactory().load.getList("social")
for (ConfigValue cv : socials) {
Config c = ((ConfigObject)cv).toConfig();
System.out.println(c.getString("url"));
System.out.println(c.getString("logo"));
}
socialConfig.root.map does not work.
Here is my solution -
private val firstSegmentRE = """^(\w+)[\.*].*$""".r
// convert "aaa.bbb.ccc" to "aaa"
private def parseFirstSegment(fullPath: String) : Option[String] = {
if (fullPath.contains("."))
fullPath match {
case firstSegmentRE(segment) => Some(segment)
case _ => None
}
else
Some(fullPath)
}
// for all keys in white list get a map of key -> config
protected def subConfigMap(config: Config, whiteList: List[String],
configName: String) : ErrorOr[Map[String, Config]] = {
// This will traverse the whole config and flatten down to the leaves..
val leafKeys : List[String] =
config.entrySet()
.asScala
.map(e => e.getKey)
.toList
// Remove all after the first dot
val nextLevelKeys : List[String] =
leafKeys.map(parseFirstSegment)
.collect {
case Some(firstSegment) => firstSegment
}
.distinct
val keysToSearch = nextLevelKeys.filter(whiteList.contains)
// we have a list of valid first level children
// parse out subconfigs and convert to map
keysToSearch.traverseErrorOr( key =>
extractSubConfig(config, key, configName).map((key, _))
)
.map(_.toMap)
}
Where extractSubConfig is a method which produces an ERROR / Config (a scalaz disjunction) and traverseErrorOr is a method to traverse a list and either process all the elements or fail and return failed disjunction if failed at any point. This method can be done without scalaz, just posting an answer to help people if they want.
Since collection.JavaConversions has since been deprecated in favor of collection.JavaConverters (and this question is the top result on a search for how to iterate through a Typesafe Config in Scala), I wanted to suggest a more modern version of Cole's great answer:
import collection.JavaConverters._
val socialConfig = ConfigFactory.load.getConfig("social")
for ( (name: String, configObject: ConfigObject) <- socialConfig.root.asScala) {
println(name) // prints "twitter" or "facebook"
val config = configObject.toConfig
println(config.getString("url"))
println(config.getString("logo"))
}
To be clear, socialConfig.root.asScala yields a standard Scala Map[String, ConfigObject] and you can iterate over it however you'd like.
import collection.JavaConversions._
val socialConfig = ConfigFactory.load.getConfig("social")
val socialConfigMap = socialConfig
.root()
.entrySet()
.asScala
.map(socialEntry => {
println(socialEntry.getKey)
val socialEntryConfig = socialEntry.getValue.asInstanceOf[ConfigObject].toConfig
println(socialEntryConfig.getString("url"))
println(socialEntryConfig.getString("logo"))
})
For the config mentioned originally in question:
social {
twitter {
url="https://twitter.com"
logo="images/twitter.png"
}
facebook {
url="https://www.facebook.com"
logo="images/facebook.png"
}
}
A solution in Java is:
val socialConfig = ConfigFactory.load.getConfig("social");
ConfigList socials = socialConfig.getList("social");
Map<String, Object> map = socials.root().unwrapped();
for (Map.Entry<String, Object> cv : map.entrySet()) {
Map<String, String> c = (Map<String, String>)cv.getValue();
System.out.println(cv.getKey());
System.out.println(c.getOrDefault("url", ""));
System.out.println(c.getOrDefault("logo", ""));
}

validation of fields in a form in scala with lift frame work

I am working with the Lift framework and Scala. I have a form to sign up to my application, and I want to validate all the fields in it. I have a snippet where I access my form values, and one validation class where I wrote my validation functions. The following code is what I've tried so far. In my Snippet:
if(validationClassObject.validateName(first_name)){
if(validationClassObject.validateName(last_name)){
if(validationClassObject.validateEmail(email)){
if(validationClassObject.validateUserName(name)){
// Adding values to the DB
S.redirectTo("/")
}
else{
S.notice("Invalid User Name")
}
}
else{
S.notice("Invalid Mail Id")
}
}
else{
S.notice("Invalid Last name")
}
}
else{
S.notice("Invalid First Name")
}
In the validationClass I wrote the validation code looks like:
//function for validating mail address
def validateEmail(email: String): Boolean =
"""(\w+)#([\w\.]+)""".r.unapplySeq(email).isDefined
//code for validating remaining fileds like above
This is working, but I know this is not the best way of coding this operation in Scala. How could I modify my code in a more scalable way? How can I use case classes here?
You could do:
def av[T,V](validationFunction: => Boolean, error: => T)(f: => V)={
if(!validationFunction) error
else f
}
def v[V](validationFunction: => Boolean, error: => String)(f: => V)=av(validationFunction,S.notice(error))(f)
import validationCalssObject._
v(validateName(last_name),"Invalid Last name"){v(validateName(name),"Invalid User Name"){...}}
av is a abstract method with T and V as result types for the error function and continue function f. v is the more specific function what expects a string for error and encapsulates the notice() call. we give f as the part in the curly braces v(validation, errormsg){/*todo when there is no problem*/}.
I can't do formatting in comments so I'll post a new answer.
def badName() = if ("name" == "") Some("bad name") else None
def badEmail() = if ("email" == "") Some("bad email") else None
val verifications = List[() => Option[String]](badName, badEmail)
val failed = verifications.flatMap(_())
if (failed.nonEmpty) {
// handle failed
} else {
// your custom logic here
}
if (badName) S.notice
else if (badEmail) S.notice
else if (badDay) S.notice
else { // everything OK...
// return a JsCmd or what else do you wanted here
}
An alternative solution can be written using Option and flatMap, without these all "if"-s hardcoded. If you're interested in that -- ask..

Liftweb - WiringUI and attributes bug using AutoComplete

My problem is happening when I use some attributes in
I have the following error displayed on my webpage:
error on line 146 at column 80: AttValue: " or ' expected
Here is the corresponding line:
try{jQuery("#F114215951950RGX25X").each(function(i) {this.innerHTML = "<span id=\"F114215951957PA3NZS\"></span>";});} catch (e) {}
Here is the code in my snippet:
import net.liftweb.util.ValueCell
import http.SHtml
import net.liftweb.widgets.autocomplete.AutoComplete
object SearchMenu {
/* A valueCell on which WiringUI is used */
val wiringCell= ValueCell(true)
/* The function called in html template */
def display = WiringUI.apply(wiringCell)(displayAjax)
/* The cell to be updated using autocomplete */
val cell = ValueCell("")
/* The function to create and display the autocomplete box */
def displayAjax(value: Boolean)(ns:NodeSeq):NodeSeq = {
def buildQueryName(current: String, limit: Int): Seq[String] = {
if (current.length == 0) Nil
else (1 to limit).map(n => current+""+n).take(limit)
}
AutoComplete("", buildQueryName _, cell.set _)
}
}
Here is the code in my HTML page:
<form class="lift:form.ajax">
<div class="lift:display"> </div>
</form>
Note: the code works if I call displayAjax(true) instead of display, that is to say, if I don't use Wiring.
Note: I think the problem might come from the fact that autocomplete uses a script that is not loaded when using Wiring UI.
It seems like I found 2 bugs in lift for the autocomplete widget. Here is the thread in the official lift mailing list: https://groups.google.com/forum/?fromgroups#!topic/liftweb/Zu5DBqSSW4U