Field Constructors - Get Form Constraints with Repeating Elements - forms

My ultimate goal is to append * to every label where the associated field is mandatory.
The problem is that within the FieldElements.constraints there are no constraints if I have a list of elements.
Here is a code sample showing form with a list of addresses:
object Forms {
val testForm: Form[TestForm] = Form {
val address = mapping(
"addressLine" -> nonEmptyText,
"city" -> nonEmptyText,
"country" -> nonEmptyText.verifying(nonEmpty)
)(Address.apply _)(Address.unapply)
// to form mapping for TestForm
mapping(
"mand_string" -> nonEmptyText,
"non_mand_string" -> optional(text),
"address_history" -> list(address) // repeated values are allowed for addresses
)( TestForm.apply _ )( TestForm.unapply )
}
}
Here is the field constructor logic to add *s to mandatory fields:
object MyHelpers {
implicit val myFields = FieldConstructor( fieldElem => {
Logger.debug( "Label = " + fieldElem.label )
Logger.debug( "Constraints = " + fieldElem.field.constraints )
val new_elem = fieldElem.copy( args = appendAsteriskToMandatoryFields(fieldElem) )
views.html.fs1(new_elem)
})
/** Adds a * to the end of a label if the field is mandatory
*
*/
private def appendAsteriskToMandatoryFields(fieldElem: FieldElements): Map[Symbol,Any] = {
fieldElem.args.map{ case(symbol, any) =>
if(symbol == '_label && isRequiredField(fieldElem)){
(symbol, any + "*")
} else {
symbol -> any
}
}
}
/** Does this element have the constraint that it is required?
*
*/
private def isRequiredField(fieldElem: FieldElements): Boolean = {
fieldElem.field.constraints.exists{case(key, _) => key == "constraint.required"}
}
}
I expect to see *s appended to all form elements except non_mand_string but here is the resulting page: http://s29.postimg.org/5kb5u2hjb/Screen_Shot_2014_08_05_at_1_49_17_PM.png
Only man_string has a * appended. None of the address fields have *s as expected.
Here is the output of the logs:
[debug] application - Label = Mandatory String
[debug] application - Constraints = List((constraint.required,WrappedArray()))
[debug] application - Label = Non-Mandatory String
[debug] application - Constraints = List()
[debug] application - Label = Address Line
[debug] application - Constraints = List()
[debug] application - Label = City
[debug] application - Constraints = List()
[debug] application - Label = Country
[debug] application - Constraints = List()
Is it possible to assign these constraints so I don't have to manually add *s to every list instance in my application?
Thank you in advance.

I've been struggling with this problem today: I'm using #repeat helper and I've found the constraints being tied to the single repeated field's name (i.e. "field_name") instead of each repeated field (i.e. "field_name[0]")
The solution I've found so far is just to rebuild the field, using the right constraints.
#repeat(theForm("repeated_field")) { field =>
#myInputNumber(
Field(
form = theForm, name = "name",
constraints = theForm.constraints.find(_._1 == "repeated_field").get._2,
format = field("field_name").format,
errors = field("field_name").errors,
value = field("field_name").value
))
}
Of course this code is just an example, you should probably check for empty Constraints or existing ones.
Hope to be helpful,
Pietro

Related

How to reset a table

It's possible to reset a table scala swing or remove it from the container after clicking on a button ?
I've tried to create a val with that table but I have always a new table stacked under the old
Here is the code :
// here is the most crucial part when the user click on the button, it will append a new table but if we want to start again, it will append bottom of the old one and I want here a kind of reset or removing of table
contents = new BoxPanel(Orientation.Vertical) {
contents += new Label("Hello, you're welcome")
contents += Button("Query") {
val query: ScrollPane = new ScrollPane(changeCountry())
contents -= query
Try {
contents += query
}.getOrElse(Dialog.showMessage(contents.head, "Incorrect input ! This seems that input isn't in that list, write a different code or country"))
}
// this part will ask to the user to write text to the input to display the table in function of the parameter of my function
def changeCountry(): Table = {
val text = Dialog.showInput(parent = contents.head, message = "Write a code of a country or a country", initial = "test")
text match {
case Some(s) => airportRunwayByCountry(s)
}
}
// this below part creates the table
def airportRunwayByCountry(code : String): Table = {
val headers = Seq("Airport","Runway linked")
val rowData = Functions.findAirportAndRunwayByCountry(code).map(x => x.productIterator.toArray).toArray
val tableAirportRunway = new Table(rowData,headers)
tableAirportRunway}
}
Solved with method "remove" of containers
Here is the code :
Try {
if(contents.length == 3 \\ number of items in my Box) {
\\ at this moment, only add the table because none other table exists
contents += new ScrollPane(changeCountry())
}
else {
contents -= contents.remove(3) \\get the id of the old table and remove it at this position
contents += new ScrollPane(changeCountry()) \\ at this moment, this content will have the id n°2, and the loop can start over without errors
}

How can I reduce the redundancies of the fields handling of feed handler

I am subscript to a message feed for a number of fields, I need to set the values from the feed to the domain object and have code like below:
if (map.contains(quoteBidPriceAcronym)) {
quote.bid.price = Some(map.get(quoteBidPriceAcronym).get.asInstanceOf[Number].doubleValue());
quote.changed = true;
}
if (map.contains(quoteBidSizeAcronym)) {
quote.bid.size = Some(sizeMultipler() * map.get(quoteBidSizeAcronym).get.asInstanceOf[Number].intValue());
quote.changed = true;
}
if (map.contains(quoteBidNumAcronym)) {
quote.bid.num = Some(map.get(quoteBidNumAcronym).get.asInstanceOf[Number].shortValue());
quote.changed = true;
}
if (map.contains(quoteAskPriceAcronym)) {
quote.ask.price = Some(map.get(quoteAskPriceAcronym).get.asInstanceOf[Number].doubleValue());
quote.changed = true;
}
if (map.contains(quoteAskSizeAcronym)) {
quote.ask.size = Some(sizeMultipler() * map.get(quoteAskSizeAcronym).get.asInstanceOf[Number].intValue());
quote.changed = true;
}
if (map.contains(quoteAskNumAcronym)) {
quote.ask.num = Some(map.get(quoteAskNumAcronym).get.asInstanceOf[Number].shortValue());
quote.changed = true;
}
if (map.contains(quoteExchTimeAcronym)) {
quote.exchtime = getExchTime(String.valueOf(map.get(quoteExchTimeAcronym).get));
}
It look pretty redundant, any suggestion to improve it?
You can do something like:
map.get(quoteBidPriceAcronym).map { item =>
quote.bid.price = item.map(_.asInstanceOf[Number].doubleValue())
quote.changed = true
}
Other issues might be better to fix outside. E.g. why map[quoteBidPriceAcronym] is storing an Option, if your code assumes it's not going to be None?
Something like this perhaps?
val handlers = Map[String, Number => Unit] (
quoteBidPriceAcronym -> { n => quote.bid.price = Some(n.doubleValue) },
quoteBidSizeAcronym -> { n => quote.bid.size = Some(sizeMultipler() * n.intValue },
etc. ...
)
for {
(k,handler) <- handlers
values <- map.get(k).toSeq
quote.chanded = true
_ = handler(n.asInstanceof[Number])
}
Personally, I don't like code changing an object state (quote) but this is a question on Scala, not functional programming.
That said I would reverse the way you are using you map map keys. Instead of checking whether a value exists to perform some action, I'd have a map from your keys to actions and I'd iterate over your map elements.
e.g (assuming map is of the type Map[String, Any]):
val actions: Map[String, PartialFunction[Any, Unit]] = Map(
(quoteBidPriceAcronym, {case n: Number => quote.bid.price = Some(n.doubleValue())}),
(quoteBidSizeAcronym, {case n: Number => quote.bid.size = Some(sizeMultipler() * n.doubleValue())}),
...
...
)
for((k,v) <- map; action <- actions.get(k); _ <- action.lift(v))
quote.changed = true;
The for construct here iterates over map key-values, then (next level of iteration, over the possible action available for the key. If an action is found, which is a partial function, it gets lifted to make it a function from Any to Option[Unit]. That way, you can iterate in an additional inner level so quote.changed = true is only run when the action is defined for v.

how to get autocomplete text box value using lift web

I am using Lift web framework.
I am implementing an auto-complete text box. When I enter some value in the box a drop-down list opens. If I select a value from that list, only then I am able to access value of text box. If I write a value by myself then I get an empty value.
My code :
var friend_name=""
"#bdayReminder" #> AutoComplete("",
getAllName _,
value => takeAction(value),
List("minChars" -> "3"))
private def takeAction(str: String) {
friend_name = str
}
Please suggest a solution
Disclaimer: I'm the author of following library.
I think lift-combobox could achieve what you want, since it has a feature that let user created the value on-the fly. It use select2 jQuery plugin, so you will have a nice look and feel for the drop-down menu.
For example if you need to get the user-created value, it will simply as the following, note that we usually using Option[T] to denote that the value may not be presented, for example, the user may not selected any item in drop-menu at all:
var friend_name: Option[String] = None
val friendsMenu = new ComboBox(
default = None,
allowCreate = true
) {
// This is where you build your combox suggestion
override def onSearching(term: String): List[ComboItem] = {
val names = List(
ComboItem("f1", "Brian"), ComboItem("f2", "Alice"),
ComboItem("f3", "Luke"), ComboItem("f4", "Smith"),
ComboItem("f5", "Brandon")
)
names.filter(_.text.contains(term))
}
override def onItemSelected(selected: Option[ComboItem]): JsCmd = {
friend_name = selected
// The returned JsCmd will be executed on client side.
Alert("You selected:" + selected)
}
// What you want to do if user added an item that
// does not exist when allowCreate = true.
override def onItemAdded(text: String): JsCmd = {
friend_name = Some(text)
}
}
"#bdayReminder" #> friendsMenu.combobox

How to create read-only field in View

I have a form with 2 fields - empno and name. Both fill up with default value. When display in view, I want empno is read-only and name is editable.
In view creation, I am using #leaveform.value.get.empno to display ready-only and work fine. The problem only occur during insert with error ([NoSuchElementException: None.get]).
Questions:
The problem is return error does not have value property. What else could I use to get the value?
Could I skip #inputText for read-only field?
Refer below my code:
// ***** CONTROLLER *****//
val leaveform = Form[LeaveModel](
mapping(
"empno" -> nonEmptyText,
"name" -> nonEmptyText
)((no, empno) => LeaveModel(empno, name))
((leave: LeaveModel) => Some(leave.empno, leave.name))
)
def create = withAuth { username => implicit request =>
// Define default values
val empno = "STUDENT"
val name = ""
// Set default values
val filledForm = leaveform.fill(LeaveModel(empno,name))
Ok(html.leave.form(filledForm))
}
def insert = Action (
implicit request => {
leaveform.bindFromRequest.fold(
error => {
BadRequest(html.leave.form(error)) // Question 1. Here is the error.
},
leave => {
LeaveModel.insert(leave)
Redirect(routes.indexController.index())
}
)
}
)
// ***** VIEW START***** //
#(leaveform: Form[LeaveModel])
#leaveform.value.get.empno
#helper.form(
action = (routes.LeaveController.update(oid)),
'id -> "leaveform") {
#inputText(leaveform("empno")) // Question 2.
#inputText(leaveform("name"))
}
It is not mandatory to use the form helpers. If you use them you can pass the attribute readonly or style the field with CSS to make it look read only.
Twitter bootstrap disabled by CSS:
#inputText(
editForm("createdOn"),
'id -> "createdOn",
'class -> "input-xlarge disabled",
'_label -> Messages("createdOn"),
'_help -> ""
)
Pass optional attribute: readonly
#inputText(
editForm("createdOn"),
'id -> "createdOn",
'class -> "input-xlarge",
'_label -> Messages("createdOn"),
'readonly -> "readonly",
'_help -> " This is read only"
)
You can also don't resend the field, but display its value:
<span class="date">Created on: #editForm("createdOn").value</span>
Update 2018-01-24
Play field is now returning a Optional, see the docs. This means you can get the value from the field like:
#form("fieldName").getValue.get (can throw a NPE)
#form("fieldName").getValue.getOrElse("defaultValue")
Try using the Flash context when returning a form to the user:
def create = withAuth { username => implicit request =>
// leaveForm initialization as before
// use form data from flash if present
val form = if (flash.get("error").isDefined)
leaveForm.bind(flash.data)
else
leaveForm
Ok(stakeholders.register(form))
}
def insert = Action { implicit request =>
leaveForm.bindFromRequest.fold(
hasErrors = { form =>
Redirect(routes.Leaves.create). // put correct route here
flashing(Flash(form.data) +
("error" -> Messages("validation.errors"))) // customize error message
},
leave => {
LeaveModel.insert(leave)
Redirect(routes.indexController.index())
}
)
}
HTH, Erich

Lift Framework - problems with passing url param to Snippet class

I am trying to do a simple case of /author/ and get the Lift to build a Person object based on the id passed in.
Currently i have an Author snippet
class Author(item: Person) {
def render = {
val s = item match { case Full(item) => "Name"; case _ => "not found" }
" *" #> s;
}
}
object Author{
val menu = Menu.param[Person]("Author", "Author", authorId => findPersonById(authorId), person => getIdForPerson(person)) / "author"
def findPersonById(id:String) : Box[Person] = {
//if(id == "bob"){
val p = new Person()
p.name="Bobby"
p.age = 32
println("findPersonById() id = " +id)
Full(p)
//}else{
//return Empty
//}
}
def getIdForPerson(person:Person) : String = {
return "1234"
}
}
What i am attempting to do is get the code to build a boxed person object and pass it in to the Author class's constructor. In the render method i want determine if the box is full or not and proceed as appropriate.
If i change
class Author(item: Person) {
to
class Author(item: Box[Person]) {
It no longer works but if i leave it as is it is no longer valid as Full(item) is incorrect. If i remove the val s line it works (and replace the s with item.name). So how do i do this. Thanks
The Box returned from findPersonById(id:String) : Box[Person] is evaluated and if the Box is Full, the unboxed value is passed into your function. If the Box is Empty or Failure the application will present a 404 or appropriate error page instead.
You can try double boxing your return if you want to handle this error checking yourself (so that the result of this method is always a Full Box).
def findPersonById(id:String) : Box[Box[Person]] = {
if(id == "bob"){
val p = new Person()
p.name="Bobby"
p.age = 32
println("findPersonById() id = " +id)
Full(Full(p))
}else{
return Full(Empty)
}
}
and then this should work:
class Author(item: Box[Person])