Spray - Parsing forms with checkboxes - forms

I am setting up a simple API part of which accepts POST requests via form submission. The form requires the user to select one or more checkboxes all sharing the same name e.g.
<form>
<input type='text' name='textval'>
<input type='checkbox' name='cbox' value='val1'> Value 1
<input type='checkbox' name='cbox' value='val2'> Value 2
<button type='submit'>Submit</button>
</form>
I am attempting to handle the request in Spray like so:
path("mypath") {
post {
formFields('textval, 'cbox) { (textval, cbox) =>
// Now what?
}
}
}
I cannot find documentation in the Spray Docs on how to handle such inputs. In fact, this seemed to be an issue that has now been fixed, but I am unsure how to handle this form field with the Spray API

I have found a somewhat hacky solution to meet my needs. I have changed the names of my checkboxes to represent the values associated with them, and I use optional form fields in my spray route
Here is my new form
<form>
<input type='text' name='textval'>
<input type='checkbox' name='cbox1' value='val1'> Value 1
<input type='checkbox' name='cbox2' value='val2'> Value 2
<button type='submit'>Submit</button>
</form>
The route changes accordingly
path("mypath") {
post {
formFields('textval, 'cbox1.?, 'cbox2.?) { (textval, cbox1, cbox2) =>
complete(s"textval:'$textval', cbox1:'$cbox1', cbox2:'$cbox2'")
}
}
}
The optional fields get mapped to type Option[String] which can then be easily checked with .isEmpty or something similar to determine whether the checkbox was checked.
However, one issue is that it will allow forms to be posted with no checkboxes selected since they are all optional. You could perhaps set one as default.

Related

How to bind a form field to the specific submit button in play framework? (form for POST request)

Basically I want to have two buttons in my view html template, and evaluate the Int param in my form for POST-request depending on which button has been clicked.
Like if button-number-1 was clicked I want my numParam to be 1
and if button-number-2 was clicked I want my numParam to be 2
Controller code:
case class Data(textParam: Option[String], numParam: Option[Int])
val form = Form(
mapping(
"textParam" -> optional(text),
"numParam" -> optional(number)
)(Data.apply)(Data.unapply)
)
View code:
#helper.form(helper.CSRF(routes.MyController.display)) {
#helper.inputText(form("paramA"))
<input type="submit" value="I want numParam to be 1">
<input type="submit" value="I want numParam to be 2">
}
Would appreciate any help!
I don't know whether this can be done with Play directly, so I propose to add some client-side JS into the mix.
What you could do:
Delete the <input type="submit" ...>, because it does not give you the possibility to modify form content before submission
add two <button>s instead
add a hidden input numValue
use javascript (in this case: jquery) to set the value of the hidden input when one of the buttons is clicked
submit the form using javascript
Something along these lines maybe (warning: untested):
#helper.form(helper.CSRF(routes.MyController.display), 'id -> "myForm") {
#helper.inputText(form("paramA"))
<button id="submit_numValue1">I want numParam to be 1</button>
<button id="submit_numValue2">I want numParam to be 2</button>
<input type="hidden" id="hiddenNumValue" name="numValue" value="0">
}
<script>
// add an `onclick` handler to first button
$('#submit_numValue1').click(function() {
// set hidden input to '1'
$('#hiddenNumValue').val("1");
// submit the form
$('#myForm').submit();
});
// add an `onclick` handler to the second button
$('#submit_numValue2').click(function() {
// set hidden input to '2'
$('#hiddenNumValue').val("2");
// submit the form
$('#myForm').submit();
});
</script>
As mentioned above, this requires that jquery is "imported" on the client-side as a javascript library.
No guarantee that this is the most idiomatic way to solve it in Play, but this answer seems to indicate that this approach is at least not uncommon.

How to edit an item which is created using a single form on Angular2?

I am new to angular2 & I have a form which can add more item to a page (item with name & decription). This works really well, I can keep on adding new item to my list.
However, each of this item has its own edit & delete. How can I edit and delete each of the item using that only 1 form?
<form #formExperiencesRef="ngForm">
<label for="name">name</label>
<input id="name" type="text" name="fruit [(ngModel)]="formData.name">
<label for="description">description</label>
<input id="description" type="text" name="fruit [(ngModel)]="formData.description">
<button (click)="onSubmit(formExperiencesRef.value)"></button>
</form>
This single form is what I use to keep on adding new item. And now I find it hard to edit the item that I created using this. Can someone help me?
Often I would advise to go with a reactive form for all it's benefits, but if your form is this simple a template driven approach can be sufficient.
First of all I see problem in your form. Your name attributes are the same for both fields, this will mean that they are evaluated as one and the same. I would actually name them as for how your formData object looks like, and then just push the form value as is to the array. I'll just use one way binding here for the sake of the editing of item. Also pass the form object in submit.
How we can edit can be done numerous ways. Here we'll utilize the index of your list (assumingly it's an array).
<form #formExperiencesRef="ngForm" (ngSubmit)="onSubmit(formExperiencesRef.value)">
<input name="name" [ngModel]="formData.name">
<input name="description" [ngModel]="formData.description">
<button type="submit">Submit</button>
</form>
Your list:
<div *ngFor="let item of items; let i = index">
{{item.name}} <button (click)="edit(item, i)">Edit</button>
</div>
And in the TS, we can use #ViewChild to reference our form, which I am using to reset the form:
#ViewChild('formExperiencesRef') formExperiencesRef: NgForm;
and your methods for editing and saving a new item:
formData = {};
items = [];
index = null; // used to store current index value of item (if exists)
edit(item, i) {
this.index = i;
this.formData = item;
}
onSubmit(val) {
// check if index exists, if not it's a new item
if(this.index == null) {
this.items.push(val)
} else {
this.items[this.index] = val;
}
// reset index & form
this.index = null;
this.formExperiencesRef.reset();
}
DEMO: http://plnkr.co/edit/ksHp10WwaDg4AQjwDf2d?p=preview
For the future, I really suggest you check out reactive forms, you have tighter control over your form, handle validations easier and a big,big advantage to me is especially if you are dealing with nested components. Reactive forms can be confusing in the beginning, but it's worth it! :)

Play framework: empty body in post request

Helo! I have a following code:
def foo = Action { request =>
Ok(request.body.asText.getOrElse("no body"))
}
In frontend have a form like this:
<form action="#controllers.routes.Application.foo()" method="POST">
<input name="name" type="text">
<input name="surname" type="text">
<button type="submit">
</form>
If I fill the form and click submit, they gives me result: no body.
But if I add brakepoint in debugger to Ok(..), they shows me, that body is not emty.
AnyContentAsFormUrlEncoded(Map(name -> ArrayBuffer(123), surname -> ArrayBuffer(123)))
Why, that doesn't give me body as text, or as anything else, and how I can get them?
Given your form and your debugging output, you should be using asFormUrlEncoded.
You'll need the following as well:
<form ... enctype="text/plain">
to specify that the payload is plain text. Then the .asText will work.
Your content type probably is something other than text/plain. The BodyParser will only return a result, when the content type is the expected.
edit: Instead of asText try asRaw.

fatfree framework, repopulate fields after unsuccessful validation

I have GET route which shows the contact form, and POST route when user submits the form, then in my controller method I do some validation tests on data being submitted.. how would I now send user back to form if data is not valid, with entered data being re-populated in form fields?
I know I can use isset(#POST.fieldname) in my template, but what's the right way of
sending entered data back to that view, and how to redirect user back to the
form? Is the f3->reroute method right way of doing that?
I think you can take as a rule to include input data inside your form views. This way, any form view will be easily reusable with any source of data.
For example:
Your form view:
<form action="" method="post">
<input type="text" name="email" value="{{ ##input.email }}"/>
<input type="text" name="message" value="{{ ##input.message }}"/>
<button type="submit">Submit form</button>
</form>
Your controller class:
class myController {
function get($f3) {
$this->renderForm();
}
function post($f3) {
$post=$f3->clean($_POST);
//validate form data here
if ($form_validated) {//valid form data
} else //invalid form data
$this->renderForm($post);
}
protected function renderForm($input=array()) {
$tpl=Template::instance();
//variant 1:
echo $tpl->render('form.html','text/html',array('input'=>$input))
// or variant 2:
Base::instance()->set('input',$input);
echo $tpl->render('form.html');
}
}
In some other contexts, you can feed a form view with data coming from a db mapper (for example when editing an entry from a back-office): $this->renderForm($mapper->cast())

Play Framework Multiple QueryStrings

I can get one queryString from the template however, never managed to get two.
This is my controller
def get = Action { implicit request =>
val requestedProviderName = request getQueryString "providerName"
val requestedReleaseId = request getQueryString "releaseId"
}
Like that my router produces
Here is my router.conf : http://localhost:9000/fail?providerName=oneProviderName
this is all correct but I want to pass more than one option.
GET /fail #controllers.mycontroller.get
What I have as a view is so basic,
#helper.form(routes.mycontroller.get) {
<select name="providerName" class="selectpicker" data-live-search="true">
#for((providerName, failedReleasesNumber) <- providers){
<option id="selectedvalue" value="#providerName" selected="selected">
#providerName, #failedReleasesNumber
</option>
}
</select>
<div class="row-fluid">
<div class="span6">
<label>Start Date: <input type="date" id="startDate"></label>
<label>End Date: <input type="date" id="endDate"></label>
<label>Release Id: <input type="number" id="releaseId"></label>
<label>Results Start?!: <input type="number" id="resultStart"></label>
<label>Max Results: <input type="number" id="maxResults"></label>
<input type="submit" class="btn btn-primary" value="Get Failed Releases" style="margin-top:-10px">
</div>
</div>
}
My question is more, how I can define these inputs as I want them to be in the QueryPath.
I have searched the web however, couldn't find a solution. Everyone written stuff about router but how to define them in template is unanswered or I am missing something completely. I am using Play Framework 2.1.1 with Scala
For question 1:
To use url like http://localhost:9000/fail?providerName="xyz"&secondQueryString="abc" define like this in routes file
GET /fail controllers.mycontroller.get(providerName: String, secondQueryString: String)
and modify get method signature like get(providerName: String, secondQueryString: String)
For question 2:
When the form action is defined for GET method then by default all the input fields will be passed in query string. Just ensure using same query string names defined for url path (in routes file) and the name used in the html file.
Well I have found my answer, as it is answered before the operation should be GET
However, e.g.
<input type="number" name="maxResults" id="maxResults">
Just id of input field is not enough thus, there should be name field as well and after everything is okay. Even there is no need for input variables to the functions. You can get the variables like
val requestedProviderName = request getQueryString "providerName"
Which returns an optional value of input variable in the template(view).