One text input and a submit button in scala - forms

I'm looking for a solution to accomplish the same thing as in this similar question:
HTML forms with java Play Framework 2
But in Scala. Is there a way to do this? I just have one text field and a submit button. I want to get the value from the text field when pressing my button and pass this value to backend code.

object MyController extends Controller {
val submissionForm = Form(
single("myvalue" -> text)
)
def myaction = TODO //as like #mbarlocker told before
}
On such a simple case of a single input, you don't have to use mapping in form definition and you don't have to use any template helper at the input. Template helpers are useful but in this case you get freedom over the layout, without writing a custom field constructor. For example, if you need to place the the submit button next to the input field, just write something like this:
#form(routes.Application.myaction, 'class -> "form-inline") {
<input type="text" id="myvalue" name="myvalue" value="#submissionForm.data.get("myvalue")">
<input type="submit" class="btn" value="Submit :)">
}

Basically, set up the view & form the same way as in HTML forms with java Play Framework 2, and then put this in your controller.
object MyController extends Controller {
case class Submission(value: String)
val submissionForm = Form(
mapping(
"value" -> text
)(Submission.apply)(Submission.unapply)
)
def myaction = Action { implicit request =>
submissionForm.bindFromRequest().fold(
formWithErrors => {
// do something with the bad form, like reshow the view
Ok("got a bad form")
},
submission => {
// do something with the submitted form
Ok("got " + submission.value)
}
)
}
}
Play 2.1 Forms Documentation

Related

how to make mapping function work if the incoming request has less fields than expected

I am experimenting with play/scala. I have following two case classes and I want to map data from form into this model
case class User (
name:String,
age:Int,
female:Boolean,
address:Address
)
case class Address (
fullStreet:String,
county:String,
country:String
)
In controller class, I have following mapping function and action defined
val userForm = Form((mapping("name"->text,
"age"->number,
"female"->boolean,
"address"->mapping("fullStreet"->text,
"county"->text,
"country"->text)(Address.apply)(Address.unapply)
)(User.apply)(User.unapply)))
def post = Action { implicit request =>
val u:Form[User] = userForm.bindFromRequest
Ok(views.html.dataIndex(u))
}
I am facing the following issue: To make the complete code work, I have to create a form which contains all the fields required in mapping as follows:
<h1>Feed User Data</h1>
#helper.form(action=routes.Data.post){
#helper.inputText(userForm("name"))
#helper.inputText(userForm("age"))
#helper.checkbox(userForm("female"))
<fieldset>
#helper.inputText(userForm("address.fullStreet"),'_label -> "Full Street")
#helper.inputText(userForm("address.county"),'_label -> "County")
#helper.select(userForm("address.country"),Seq(""->"---",
"United Kingdom"->"UK",
"France"->"FR") )
</fieldset>
<input type="submit" name="send" value="submit"/>
}
If I create a form with say only input field for name, then bindFromRequest returns None instead of mapping only name field. Is there a way in which the form can contain less fields than required in mapping. I am not talking about fields in form with empty/optional values. I do not want to put the fields in the form at all.
I usually create a case class that represents the form data (probably not all the info from the domain class), and in the controller/service I create the domain entity using my own rules (for instance, a default value for a field not represented on the form)

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".

Play framework null checkbox list

I am new to play framework having some dificulties in accesing check box in controller. My view is:-
#(img:Form[Image])
#helper.form(action = routes.Application.abc) {
<li><input name="item[0]" value="pt" type=checkBox></li>
<li><input name="item[1]" value="sumit" type=checkBox></li>
<p>
<button type=submit id=imgButton>submit</button>
</p>
}
My conntroller is:-
def abc = Action{
implicit request =>
val values =ImageForm.bindFromRequest.get
println("mapinggg"+values)
Ok("hi")
}
My case class to handle checkbox is:-
case class Image (desc:List[String])
and form is
val ImageForm =Form(
mapping(
"desc" -> list(text)
)(Image.apply)(Image.unapply)
)
But it returns nill when I click on submit by selecting checkbox?
It gives output as Image(List()) but I want list of selected checkboxes
The names of your inputs ("item" - without the indices) needs to match the key of your list mapping (here given as "desc") for the binding to succeed. It should work if you change the ImageForm mapping to:
val ImageForm =Form(
mapping(
"item" -> list(text)
)(Image.apply)(Image.unapply)
)
Note that the actual field name in your case class - "desc" - should not matter here.

PlayFramework 2.x - Forms / Associating error message to one element of a tuple

I've got this form mapping:
val myElement = Form(
mapping(
"title" -> nonEmptyText,
"schedule" ->
tuple("startSchedule" -> jodaDate("dd/MM/yyyy HH:mm"),
"endSchedule" -> jodaDate("dd/MM/yyyy HH:mm"))
.verifying(MyValidator().checkForEndScheduleConsistency("error.schedule")),
)(MyElement.apply)(MyElement.unapply)
)
MyElement class:
case class MyElement(title: String, schedule: (Datetime, Datetime))
MyValidator class:
def checkForEndScheduleConsistency(errorMsg: String) =
Constraint[(DateTime, DateTime)]("constraint.schedule", errorMsg) {
schedule =>
MyDomainValidator().checkForEndScheduleConsistency(schedule._1, schedule._2, Messages(errorMsg)) match {
case Success(s) => Valid
case Failure(f) => Invalid(ValidationError("custom error string from `f`"))
}
}
Requirement: An error message must be associated to the field schedule.endSchedule(tuple's element) if the schedule is inconsistent according to MyValidator object.
However, in order to have both required elements (startSchedule and endSchedule) available for checkForEndScheduleConsistency method, I can't apply a verifying method directly on the nested tuple's element named endSchedule. Instead, I have to apply one on the whole tuple in order to include startSchedule variable, as shown in the code snippet.
The drawback is that the error is not mapped to endSchedule but to schedule (that doesn't represent anything in my HTML form), and so nothing is displayed to screen when an inconsistent schedule appears.
Therefore, I have to use this "workaround" to achieve my requirement using Form's withError method:
def create = Action {
implicit request =>
myForm.bindFromRequest.fold(
myFormWithErrors => {
myFormWithErrors.error("schedule") match { //check for the presence of potential schedule error
case Some(e) => {
BadRequest(views.html.create_element("Create an element", myFormWithErrors.withError("schedule.endSchedule", Messages("error.schedule"))))
}
case _ => BadRequest(views.html.create_element("Create an element", myFormWithErrors))
}
},
myForm => {
treatSubmittedMyForm(myForm)
}
)
}
=> Very ugly and anti-DRY.
Is there a way to apply verifying on the tuple and despite of that, apply the error message to a nested tuple's element? In my case, on endSchedule.
Probably the best solution is to replace the default helpers provided by Play when rendering the field by one of your own making, as per documentation:
#(elements: helper.FieldElements)
<div class="#if(elements.hasErrors) {error}">
<label for="#elements.id">#elements.label</label>
<div class="input">
#elements.input
<span class="errors">#elements.errors.mkString(", ")</span>
<span class="help">#elements.infos.mkString(", ")</span>
</div>
</div>
That way you can manually replace the error reference to the right element. Not extremely nice but you should be able to create a reusable tag from it.
The best solution is to precise the global (to schedule) "error" key into the more specific generated input directly, here the schedule.endSchedule input:
#inputText(hobbyForm("schedule.endSchedule"), 'id -> "schedule.endSchedule", '_error -> hobbyForm.error("schedule")
Notice the part: '_error -> hobbyForm.error("schedule")

Lift: How to bind a Hyperlink to a serverside method

I am new to Lift, I want to use hyperlink instead of submit button. I am able to bind my submit button with a server-side method use CSS Selector. for example:
def render = {
// define some variables to put our values into
// process the form
def process() {
do something....
}
}
"type=submit" #> SHtml.onSubmitUnit(process)
}
I want to use hyperlink to submit my form instead of submit button. How can I bind hyperlink with process()(server-side) method.
Thanks,
Puneet
In this instance you would probably want to use SHtml.ajaxCall and supply the form information as the JsonContext (i.e. not bound with CSS selectors):
def ajaxCall (jsCalcValue: JsExp, jsContext: JsContext, func: (String) ⇒ JsCmd) : (String, JsExp)
Alternativly you could use SHtml.a:
def a (func: () ⇒ JsObj, jsonContext: JsonContext, body: NodeSeq, attrs: ElemAttr*) : Elem
Failing that you should look at the available methods in SHtml (jsonForm would be another one to look at) and see which one best fits your use case. I would encourage you to pick up a copy of Lift in Action which discusses how the function binding works, as I think you have miss-understood it in relation to the request/response cycle.