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

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)

Related

how to create a Form from an object of case class

I am using fold method of form as follows
def regSubmit = Action { implicit request =>
userForm.bindFromRequest.fold({
formWithErrors=>BadRequest(views.html.Error("Registration failed")( formWithErrors.errors))
},
{
userData=>Ok(views.html.regconf("Registration Successful")(**//here I want to send a Form, not data from the form**))
})
How can I create Form from a tuple or single variable, a class or a case class?
userForm will (usually?) be defined as a val, so immutable. It holds the mapping (this field name into a variable in this position of this type, ...) When you use bindFromRequest.fold you are not changing userForm, you are using the mapping information in userForm to generate a new instance of your case class, say userData (or a version of the form with errors in it). Each time you execute that method, you will get a new instance of userData.
userForm.fill(userData) returns a new form instance, a populated instance of the form, so also does not change userForm itself.

grails: custom validation of just one field/property

I have a "Thing" domain class, where each Thing has an record number (which is not the automatically generated id), that the user will use to access a Thing:
class Thing {
...
String recordNumber
...
}
There is a form to look for a Thing, knowing its recordNumber:
<g:form action="search">
<input name="recordNumber">
<g:submitButton name="btn" value="go to this Thing"/>
</g:form>
I would like to use a validation process in this form: if the recordNumber is not found (Thing.findByRecordNumber(recordNumber) == null), then the input field must turn in red, and a tooltip must show the error message "record number not found".
As far as I know/read (I'm a grails rookie), this has to be written as a constraint in the Thing class:
static constraints = {
recordNumber validator: { n -> Thing.findByRecordNumber(recordNumber) }
}
The problem is: I do not have in this form all the "Thing" properties to populate, just the recordNumber one, so I just can't call
new Thing(params).validate()
How to call validation on just one field, not on the whole object ?
If this is your main question, although I see others there:
"How to call validation on just one field, not on the whole object ?"
You can pass a list of values to validate and it will only validate those properties
new Thing(params).validate(["recordNumber"])
http://grails.org/doc/latest/ref/Domain%20Classes/validate.html
Validation is for constraints for domain class properties. You need an action in your controller:
def search = {
if(params.recordNumber && Thing.findByRecordNumber(params.recordNumber)){
redirect(action: "show", params:[id:Thing.findByRecordNumber(params.recordNumber).id])
}else{
flush.message = "No record found"
render(view:'VIEW_WITH_SEARCH_FORM')
}
}
If you want to validate without refreshing page, write a javascript code.

how to get several same object values in a Grails form

let's take a simpe domain class:
class Person { String aname }
A gsp form to let the user input a person is easy:
<g:form ...>
...
someone:<input name="aname">
...
</g:form>
... and back in the controller, to get the values, I can just write
def p = new Person(params)
Now, I'd like to let the user input the data for two persons (let's say, two parents) in the same form. How to write this ? I just can't give the same name for the two input fields, but if I don't keep the original property name ("aname"), back in the controller I will have to handle by hand the bindings between the name of the property, and the form input names:
<g:form ...>
...
father:<input name="aname1">
mother:<input name="aname2">
...
</g:form>
then, in controller
def p1 = new Person(); p1.aname = params.aname1
def p2 = new Person(); p2.aname = params.aname2
Is there a way to keep the automatic binding facility even if there is several objects of same types given in a form ?
Try to use this way:
<g:form ...>
...
father:<input name="father.aname">
mother:<input name="mother.aname">
...
</g:form>
And controller:
def p1 = new Person(params.father);
def p2 = new Person(params.mother);
I suppose you are thinking of doing something like this:
<g:form ...>
...
father:<input name="aname">
mother:<input name="aname">
...
</g:form>
which would result as ?aname=Dad&aname=Mom
You can handle them in controller as below:
params.list('aname').each{eachName -> //Persist each `aname`}
Have a look at Handling Multi Parameters.
the dot notation in the "name" attribute works well for the <input> tag.
To go further, there is also a solution for the "fields" plugin: rather than using the "name" attribute, one should use the "prefix" one, as described here.
For example:
<f:field bean="mother" property="forename" prefix="mother."/>
<f:field bean="mother" property="surname" prefix="mother."/>
<f:field bean="father" property="forename" prefix="father."/>
<f:field bean="father" property="surname" prefix="father."/>
we can even write it better, with help of <f:with> tag:
<f:with bean="mother" prefix="mother.">
<f:field property="forename"/>
<f:field property="surname"/>
</f:with>
<f:with bean="father" prefix="father.">
<f:field property="forename"/>
<f:field property="surname"/>
</f:with>

Play 2: idiomatic approach to binding a form to List[Model]

I have several CRUD operations to perform, each one on a collection of models (e.g. game schedule, team roster, game result, game stats, etc.).
Up to this point in my Play experience (just a few months, 1 project live) I have been working with one-to-one form binding to model instance.
I know I can numerically index form field names, but then how to bind the posted form to List[Model]?
This is what my one-to-one binding looks like:
// abstract away bindFromRequest to make binding more concise in controllers
def bindForm[T](f: play.api.data.Form[T])(implicit r: play.api.mvc.Request[_]) =
f.bindFromRequest fold(e=> Left(e.errorsAsJson), Right(_))
and then in controllers:
val result = for {
model <- bindForm(form).right
id <- dao.create(model) as json
} yield id
what I would like to do is the same, but instead of model binding returning a single Model on success, have it return a List[Model], and pass on to overloaded DAO create/edit/delete operations.
I see that there is a list method that one can use as part of a Form mapping, but I have a feeling that that would wreak havoc with my JDBC query wrapper (ScalaQuery/Slick), whose case class/companion object mapping would likely not play well with collections properties.
For example, existing mapping of a game schedule looks like:
object CompositeForm {
import play.api.data.{Form, Forms}, Forms._
import utils.Validator.Bindings.jodaLocalTimeFormat
val mapper = mapping(
'id -> ignored(0),
'gameDate -> jodaDate,
'gameType -> optional(text),
'location -> optional(text),
'team1 -> number,
'team2 -> number
)(Composite.apply)(Composite.unapply)
val form = Form( mapper )
}
using list(gameDate), list(gameType) instead then means that form binding will return a single Composite instance whose properties are all collections -- maybe it will work, but doesn't seem nearly as clean/straightforward as working with a collection of model instances.
Ideas appreciated ;-)
The as yet documented seq() option in play form mapping was pointed out to me on Play google group by #Julien Richard-Foy
Using repeat() and seq() together allows one to repeat a form mapping, thus creating a collection of indexed foo.bar[n] formfield elements.
Example
object ScheduleForm {
import play.api.data.{Form, Forms}, Forms._
val mapper = mapping(
'composite -> seq(CompositeForm.mapper),
'note -> seq(ScheduleNoteForm.mapper)
)(Schedule.apply)(Schedule.unapply)
val form = Form( mapper )
}
and then in a view:
#repeat(_form("composite"), min=#numGames) { f=>
#inputDate(f("gameDate"), '_label-> "Game Date", 'class-> "required")
...
}

Bind a form field in Play 2.0 with a constant value?

I have a scala form with several fields.The fields in the form map to the member variables of a Java class. I want to bind one of the fields(say userId) with a value (I dont want the user to enter values for this field. Instead i want to pass this as a parameter to the scala template). However, i was unable to manually bind a form field. Any help is highly appreciated.
See the sample below for easier understanding :
`#(itemForm: Form[Item], user: User)
#import helper._
#main("Item list") {
#if(user != null) {
#form(routes.Application.newItem()) {
#itemForm("userId") = #user.id /**I want to bind the userId form field */
#inputText(itemForm("title"))
#inputText(itemForm("description"))
#inputText(itemForm("price"))
<input type="submit" value="Create">
}
}
}`
In this case it would be better to pass it as action's argument (remember to modify routes declaration)
#form(routes.Application.newItem(user.id)){
....
you can also just use common html
<input type="hidden" name="userId" value="#user.id" />
edit:
Validation in action.Note: it doesn't make sense to display errors on the page next to hidden field, so you do not need placeholders for error messages. It's up to you to pass VALID value into the hidden field. Displaying validation errors to user who can not change the value of hidden field is bad conception.
public static Result newItem(){
Form<ItemModel> itemForm = form(ItemModel.class).bindFromRequest();
if (itemForm.hasErrors(){
return badRequest(newItemView.render(itemForm));
}
itemForm.get().save();
return ok("Your new item is saved...");
}