Lift (Scala) form validation - forms

I have a question for which I couldn't find an answer anywhere in the web. I have a lift-based web application written on Scala. There is a form in my application which is driven by a snippet. I use Mapper for my models, and I use override def validations ... to define model's validation parameters. The template code looks like this:
<form class="lift:GroupManager.addGroup?form=POST&action=add">
<div class="row collapse" style="width: 30em">
<div class="small-8 columns">
<span class="group-name prefix radius"></span>
</div>
<div class="small-4 columns">
<span class="submit button small postfix radius">Add New Group</span>
</div>
</div>
</span>
</form>
The snippet code:
def addGroup = {
object groupName extends RequestVar[String]("")
def doAddGroup() = {
val group = Group.create.createdAt(new java.util.Date).groupName(groupName.is)
group.validate match {
case Nil => {
group.save
S.notice("Group '" + group.groupName.is + "' has been added successfully")
}
case errors:List[FieldError] => {
S.error(errors)
}
}
}
".group-name" #> SHtml.text(groupName.is, groupName(_), "id" -> "group-name") &
".submit" #> SHtml.submit("Add group", doAddGroup)
}
The model's validations override:
object groupName extends MappedString(this, 700) {
override def dbColumnName = "group_name"
override def dbNotNull_? = true
override def validations = valMinLen(1, S.?("Group name should not be empty!")) _ ::
valMaxLen(700, S.?("Group name should not be more than 700 characters long!")) _ ::
valUnique(S.?("Group name should be unique!")) _ ::
super.validations
}
Now everything is perfect except the fact that when invalid data is provided, S.error is used to inform user about problems. What I want to do is to highlight the form element (by applying class="error") in which the data is invalid and add a <span> element with the error message in front of the form element. Is there a simple way to do that with Lift framework? Or I should just traverse through errors:List[FieldError] and fetch field information from that array, which does not seem too elegant to me?

Related

[Play]Don't use multiple parameters in a route for display a view that takes multiple parameters

I'm writing a Play 2.3.2 application in Scala.
In My application I'm using a form representing an Integer range(start, finish).
The range class is written as following:
case class Range (
startDate: Long,
finishDate: Long)
I've defined a form like the following:
val rangeForm: Form[Range] = Form (
mapping(
"startDate" -> longNumber(min = 0, max = System.currentTimeMillis()),
"finishDate" -> longNumber(min = 0, max = System.currentTimeMillis())
)(Range.apply)(Range.unapply)
)
In my StatisticsController class I've defined three four:
calculateStatisticsOnRange(range: Range): Future[(Double, Int)]: that calculate the number and average of click of the user in a range;
getCalculateStatisticsOnRangeForm that display the form
calculateGoodAdvicesHtml that check if the form has errors
displayStatistics(average: Double, count:Int) that display the result of the search.
My form view is implemented like:
#(rangeForm: Form[recommendationsystem.models.Range])
#recommendationsystem.views.html.main("Insert a range")(recommendationsystem.views.html.nav.navbar("statistics")) {
<h1>Insert here the range</h1>
<div class="container">
#helper.form(action = recommendationsystem.controllers.manager.routes.StatisticsController.calculateGoodAdvicesHtml()) {
<label for="startDate">Start Date:</label>
<input type="number" class="form-control" placeholder = "start"
name="startDate" value="#rangeForm("startDate")" />
<input type="number" class="form-control" placeholder="finish"
name="finishDate" value="#rangeForm("finishDate")" />
<button type="submit" class="btn btn-default">Search</button>
}
</div>
}
My controller methods are implemented like:
def getCalculateStatisticsOnRangeForm = Action {
Ok(recommendationsystem.views.html.manager.statistics.forms.rangeform(rangeForm))
}
def calculateGoodAdvicesHtml = Action.async { implicit request =>
val form = rangeForm.bindFromRequest
form.fold(
errors => Future{BadRequest(recommendationsystem.views.html.manager.statistics.forms.rangeform(errors))},
range => {calculateStatisticsOnRange(range) flatMap {result => Future{Redirect(routes.StatisticsController.displayStatistics(result._1, result._2))}}}
)
}
def displayStatistics(average: Double, count:Int) = Action{
Ok(recommendationsystem.views.html.manager.statistics.goodadvise(average, count))
}
In my route file I've the following routes:
GET /statistics/good #recommendationsystem.controllers.manager.StatisticsController.getCalculateStatisticsOnRangeForm
POST /statistics/good #recommendationsystem.controllers.manager.StatisticsController.calculateGoodAdvicesHtml
POST /statistics/good/json #recommendationsystem.controllers.manager.StatisticsController.calculateGoodAdvicesJson
GET /statistics/:average/:count #recommendationsystem.controllers.manager.StatisticsController.displayStatistics(average: Double, count: Int)
I don't like to pass multiple parameters in the route of /statistics/:average/:count, there is an other way to make that?

how to attach multiple files to email using scala and liftweb

I'm trying to attach multiple files I'm writing REST Service for that but the files are not loading
my code look like below:-
HTML:-
<form action="DemoEmailAttachment" method="post">
To: <input type="text" name="to"><br>
Subject: <input type="text" name="subject"><br>
Body:<textarea rows="4" cols="50" name="body"></textarea><br>
AttachFiles:<input type="file" name="fileupload" multiple="multiple"><br>
<input type="submit" value="Send">
</form>
SCALA:-
object demoAttachment {
def sendAtchMail(request: Req) = {
val obj = request._params
println(obj)
LiftRules.handleMimeFile = OnDiskFileParamHolder.apply
val fileupload = request.uploadedFiles
println(fileupload)
val to = (obj.get("to").get)(0).split(",")
val subject = obj.get("subject").get
val body = obj.get("body").get
case class CSVFile(bytes: Array[Byte],filename:String,mime:String )
var data = Array[Byte]()
println(data.length)
val ls = fileupload.map(c => c match {
case ff:OnDiskFileParamHolder => { ff.fileStream.read(data)
val attach = CSVFile(data,ff.fileName,ff.mimeType)
val msg = XHTMLPlusImages(<p>Please research the enclosed. </p>,PlusImageHolder(attach.filename, attach.mime, attach.bytes))
Mailer.sendMail(
From("536#gmail.com"),
Subject(subject(0)),
To(to(0)),
msg
)
}
case _ => "there are no files"
} )
"success"
}
}
The XHTMLPlusImages constructor takes a vararg for the attachment parameter. So, just pass in multiple attachments, like this:
XHTMLPlusImages(NodeSeq, attach1, attach2, attach3, ...)
Also, you should verify that data actually contains what you are looking send. Unless I am mistaken, the way you are reading the data from the stream will only read in however many bytes your Array[Byte] can hold, which is probably 0. There is a good answer here that discusses reading from the InputStream to a ByteArray.

Play framework if else case Not working

Play framework if else case is not working here
If userprofile.useraccountid, useraccount.id have the same value then not views that id user on view page
my code in the view..
#(userprofiles: List[UserProfile],myFriend:models.MyFriend,userprofile:models.UserProfile,useraccount:models.UserAccount)
#helper.form(action = routes.Application.createMyFriend) {
<br/><br/><br/>
#for(userprofile <- userprofiles){
#if(userprofile.useraccountid != useraccount.id) {
<img src="#routes.Assets.at("images/img2.png")" width="200" height="200" />
<br>
<h5>#userprofile.name</h5>
<h5>#userprofile.useraccountid</h5>=<h5>#useraccount.id</h5>
<h6>#userprofile.gender</h6>
<h6>#userprofile.date_of_birth</h6>
<div class="actions">
<input type="submit" class="btn primary" value="+1 Add As Friend" title="Send Friend Request">
</div>
<br/>
}
}
}
when checking the condition the database values are views in the view page and
#if(userprofile.useraccountid != useraccount.id)
if change the condition to
#if(userprofile.useraccountid == useraccount.id)
Nothing will in the view page.
In this code when run the program the code section
<h5>#userprofile.useraccountid</h5>=<h5>#useraccount.id</h5>
The id's are same here , and that show in the view then the idea is not false.. for example 15=15.
Here the 2 id are same but the checking in the if case is not work properly... or the coding is not right.
Edit
This is in application
def listMyFriend = Action { implicit request =>
var cid=request.session.get("userId")
println("aa",cid)
if (request.session.get("userId") == None) {
Results.Redirect("/")
}
else {
val userprofiles:UserProfile=null
val userprofileId = request.session.get("userId").get.toLong//userProfileId
val userprofile = UserProfile.findUserByAccountId(userprofileId).get
println(userprofile)
/* val myfriendId = request.session.get("myFriendId").get.toLong//myFriendId
val myfriend = MyFriend.friendidByUserIsAccepted(myfriendId,true)
println(myfriend)*/
myFriendForm.bindFromRequest.fold(
errors => BadRequest(views.html.myFriend(errors, userprofile,myfriend,myfrnd)),
myFriend => {
println("errors")
val myFriendOpt = UserProfile.myFriend(userprofile.id.get)
println(myFriendOpt)
myFriendOpt match {
case None =>
}
Results.Redirect("/myFriend")
})
}
}
You have shadowing issues in your code: userprofile is both defined as a parameter of your template and as the variable you get out of the for comprehension.
#(userprofiles: List[UserProfile],myFriend:models.MyFriend,userprofile:models.UserProfile,useraccount:models.UserAccount)
here ---^
#helper.form(action = routes.Application.createMyFriend) {
<br/><br/><br/>
#for(userprofile <- userprofiles){
and here ---^
Try renaming one of the two and sort out which one you want to refer to in your if.

How can I bind snippet function to onclick event from button or image?

I have defined this function to build a list os students, each one with his name, surname and an image, that depending an attribute can be a tick or a cross. I want to assign to the tag academic(my image) the onClick event to call a function in my snippet called academic(Student s) which receives a student,and then redirect to another page. Can anyone help me??
def students(xhtml: NodeSeq) = {
def bindStudents(template: NodeSeq): NodeSeq = {
studentsList.flatMap {
s => bind("studentTag", template, "name" -> s.Name, "surname" -> s.Surname,
AttrBindParam("academic", if (s.HasIssue("Academic")) tickUrl else crossUrl, "src"),
)}
}
bind("list", xhtml, "studentsList" -> bindStudents _)
}
def academic(s:Student)={
//do some stuff with s
redirectTo("/OtherPage.html")
}
HTML CODE
<list:studentsList>
<tr>
<td> <studentTag:name /> </td>
<td> <studentTag:surname /> </td>
<td>
<img studentTag:academic=""/>
</td>
</list:studentsList>
There is a method in SHtml called 'onEvent'.
I'm not very sure how to do this in the old way, but if you are using new designer-friendly CSS binding helpers, it will be very simple:
HTML template:
<div data-lift="MySnippet">
Name:<span id="name">Student Name</span>
SurName:<div class="surname">Surname</span>
<img src="imageURL" class="clickable"/>
</div>
Here is the snippet:
class MySnippet
{
val studentName = "whatever"
val surName = "..."
def onClickCallback(s: String): JsCmd = {
// Do what ever you want, BTW, I never have idea
// what the ``s`` the lift will passed in.
Alert('Hey, you clicked me.') // This is the javascript that browser will execute when user click on the element.
}
// Note there is no argument list
def render = {
"#name" #> studentName & // Replace the HTML tag with id="name" to studentName
".surname" #> surName & // Replace the HTML tag with class=surname to surName
".clickable [onClick]" #> SHtml.onEvent(onClickCallback) // Append onClick attribute to HTML tag that has "clickable" class, and it will calle onClickCallable in your snippet.
}
}
Update
Sorry, I didn't notice that you are binding to a list. But since there is curried function, it would not be too difficult either.
HTML code:
<table data-lift="MySnippet">
<tr>
<td><span class="name">Name</span></td>
<td><span class="surname">SurName</span></td>
<td><img src="test.jpg"/> </td>
</tr>
</talbe>
case class Student(name: String, surname: String)
class MySnippet
{
def onClickCallback(student: Student)(s: String): JsCmd = {
Alert("You Click:" + student)
}
val xs = Student("Brian", "Hsu") :: Student("David", "Walter") :: Nil
def render = {
"tr" #> xs.map { student =>
".name" #> student.name &
".surname" #> student.surname &
".clickable [onClick]" #> SHtml.onEvent(onClickCallback(student)_)
}
}
}

Dynamically add textareas client-side to a form in lift

I've got a form based on the sample in http://simply.liftweb.net/index-4.2.html#toc-Section-4.2 and I was wondering if there's a way of having a button on the page that would add a textarea each time it's clicked, and then in the lift code get that as an array of strings.
What I'm picturing is something like this:
<form class="lift:OnSubmit?form=post">
Name: <input name="name"><br>
Age: <input name="age" value="0"><br>
<span id="somecomments"></span>
<input type="button" onclick="$('#somecomments').append($('<textarea cols=80 rows=10 name=comments>'))" value="Add Comment"/>
<input type="submit" value="Submit">
</form>
//in scala:
object OnSubmit {
def render = {
var name = ""
var age = 0
var comments = List("")​
def process() {
S.notice("Name: "+name)
S.notice("Age: "+age)
S.notice(comments)
S.redirectTo("/")
}
}​
"name=name" #> SHtml.onSubmit(name = _) &
"name=age" #> SHtml.onSubmit(s => asInt(s).foreach(age = _)) &
"name=comments" #> SHtml.onSubmit(comments = _) &
"type=submit" #> SHtml.onSubmitUnit(process)
}
}
But I get the compile error that the comments field is a String so I can't assign it to a List with "name=comments" #> SHtml.onSubmit(comments = _)
What's the best way to make this code work?
for prepending:
"name=comments" #> SHtml.onSubmit(comments ::= _)
for appending:
"name=comments" #> SHtml.onSubmit(comments :+= _)
Here I describe how you can add any number of fields (I have a textarea and a "related" numeric field.
You add them using jQuery and then Lift gets all the data as a json object.
Dynamically adding fields to a Lift application