How to change a form's action in Lift - scala

I am building a Lift application, where one of the pages is based on the "File Upload" example from the Lift demo at: http://demo.liftweb.net/file_upload.
If you look at the source code for that page... you see that there is a Lift "snippet" tag, surrounding two "choose" tags:
<lift:snippet type="misc:upload" form="post" multipart="true">
<choose:post>
<p>
File name: <ul:file_name></ul:file_name><br >
MIME Type: <ul:mime_type></ul:mime_type><br >
File length: <ul:length></ul:length><br >
MD5 Hash: <ul:md5></ul:md5><br >
</p>
</choose:post>
<choose:get>
Select a file to upload: <ul:file_upload></ul:file_upload><br >
<input type="submit" value="Upload File">
</choose:get>
</lift:snippet>
The idea is that when a user hits the page for the first time (i.e. a GET request), then Lift will show the form for uploading a file. When the user submits the form (i.e. a POST request to the same page), then Lift instead displays the outcome of the file being processed.
With my application, the new wrinkle is that my "results" POST view needs to also contain a form. I want to provide a text input for the user to enter an email address, and a submit button that when pressed will email information about the processed file:
...
<choose:post>
<p>
File name: <ul:file_name></ul:file_name><br >
MIME Type: <ul:mime_type></ul:mime_type><br >
File length: <ul:length></ul:length><br >
MD5 Hash: <ul:md5></ul:md5><br >
</p>
<!-- BEGIN NEW STUFF -->
Output: <br/>
<textarea rows="30" cols="100"><ul:output></ul:output></textarea>
<br/><br/>
Email the above output to this email address:<br/>
<ul:email/><br/>
<input type="submit" value="Email"/>
<!-- END NEW STUFF -->
</choose:post>
...
However, both the GET and POST versions of this page are wrapped by the same Lift-generated form, which has its "action" set to the same snippet in both cases. How can I change this such that in the POST version, the form's action changes to a different snippet?
In a typical web framework, I would approach something like this with an "onclick" event and two basic lines of JavaScript. However, I haven't even begun to wrap my mind around Lift's... err, interesting notions about writing JavaScript in Scala. Maybe I need to go down that route, or maybe there's a better approach altogether.

First, I will suggest you use Lift's new designer friendly CSS binding instead of the custom XHTML tag.
And one thing you should remember when you're using Lift's snippet, is that it is recursive, you could put an lift snippet inside another snippet's HTML block.
For example, if you wish there is another form after POST, then just put it into the block.
<choose:post>
<p>
File name: <ul:file_name></ul:file_name><br >
MIME Type: <ul:mime_type></ul:mime_type><br >
File length: <ul:length></ul:length><br >
MD5 Hash: <ul:md5></ul:md5><br >
</p>
<!--
The following is same as <lift:snippet type="EMailForm" form="post" multipart="true">
-->
<form action="" method="post" data-lift="EMailForm">
<input type="text" name="email"/>
<input type="submit" />
</form>
</choose:post>
Then deal with the email form action at snippet class EMailForm.
Finally, you may pass the filename / minetype and other information by using hidden form element or SessionVar.

I agree with Brian, use Lift's new designer friendly CSS binding.
Use two separate forms, one for the file upload and one for the submitting the email. Use S.seeOther to redirect the user to the second form when the first has finished processing.
I also prefer the new 'data-lift' HTML attribute.
File upload HTML:
<div data-lift="uploadSnippet?form=post">
<input type="file" id="filename" />
<input type="submit" id="submit" />
</div
File upload snippet:
class uploadSnippet {
def processUpload = {
// do your processing
....
if (success)
S.seeOther("/getemail")
// if processing fails, just allow this method to exit to re-render your
// file upload form
}
def render = {
"#filename" #> SHtml.fileUpload(...) &
"#submit" #> SHtml.submit("Upload", processUpload _ )
}
}
GetEmail HTML:
<div data-lift="getEmailSnippet?form=post">
<input type="text" id="email" />
<input type="submit" id="submit" />
</div
Get Email Snippet:
class getEmailSnippet {
def processSubmit = {
....
}
def render = {
"#email" #> SHtml.text(...) &
"#submit" #> SHtml.submit("Upload", processSubmit _ )
}
There's a bit more on form processing in my blog post on using RequestVar's here:
http://tech.damianhelme.com/understanding-lifts-requestvars
Let me know if you want more detail.
Hope that's useful
Cheers
Damian

If somebody comes up with a more elegant (or "Lift-y") approach within the next few days, then I'll accept their answer. However, I came up with a workaround approach on my own.
I kept the current layout, where the view has a GET block and a POST block both submitting to the same snippet function. The snippet function still has an if-else block, handling each request differently depending on whether it's a GET or POST.
However, now I also have a secondary if-else block inside of the POST's block. This inner if-else looks at the name of the submit button that was clicked. If the submit button was the one for uploading a file, then the snippet handles the uploading and processing of the file. Otherwise, if it was the send email submit button shown after the first POST, then the snippet processes the sending of the email.
Not particularly glamorous, but it works just fine.

Related

The right way to fill and test a web form in ScalaTest and FluentLenium

I'm trying to fill, submit, and test a web form in Play Framework, using ScalaTest and FluentLenium. It seems like it should be very straightforward, but I'm having all kinds of problems.
First, part of the web form in question:
<form class="signin" id="loginform" method="POST" action="/login">
<div class="form-group">
<label for="name">Email Address:</label>
<input type="email" class="form-control" placeholder="Enter Email Address" id="email" name="email" required />
...
This works fine from a real web browser. Now the problem comes when I try to fill in and submit the form:
#RunWith(classOf[JUnitRunner])
#SharedDriver(deleteCookies = false)
#SharedDriver(`type` = SharedDriver.SharedType.PER_CLASS)
class TestWebsiteAuthentication extends Specification {
"Application" should {
"login as an administrative user on the web site" in new WithBrowser with GPAuthenticationTestUtility {
browser.goTo(loginURL)
browser.fill("#email").`with`(prerequisiteAccounts.head.userIdentity) must equalTo(OK)
...
At that last line, I get an exception:
[info] x login as an administrative user on the web site
[error] 'org.fluentlenium.core.action.FillConstructor#1c25c183' is not equal to '200' (TestWebsiteAuthentication.scala:93)
[error] Expected: 200
[error] Actual: org.fluentlenium.core.action.FillConstructor#1c25c183
Any ideas what I'm doing wrong here?
I've tried taking out the "must equalTo(OK)" but this just causes the form to fail on submit -- unfortunately, I haven't been able to find ANY documentation on how to do this, so I'm basically piecing it together bit by bit. Pointers to relevant documentation would be appreciated -- there doesn't seem to be anything complete at Tyrpesafe... just "teasers" that get you started, but no depth. :-(
When you write browser.fill("#email").``with``("x#y.com"), all you're really doing is telling Fluentlenium to edit the template to add a value attribute inside the input tag.
On the other hand, OK is an HTTP status code, so comparing them will naturally yield false.
When you say you tried to submit the form and it failed, i am assuming you did something such as:
browser.fill("#email").`with`("x#y.com")
browser.fill("#password").`with`("myPass")
browser.click("#button") // this should submit the form and load the page after login
and then tried to make an assertion such as:
browser.title() must equalTo("next page") // fails because "next page" != "login page"
one suggestion is to try something like this, Before browser.click:
browser.pageSource() must contain("xyz") // this will fail
When the above assertion fails, it will print the content of browser.pageSource() to your terminal, and you'll be able to see the modifications the Fill function did to the HTML.
In my case, I observed that my pageSource() now contained the following:
<input type="text" id="email" name="email" value="x#y.com"/>
<input type="password" id="password" name="password"/>
Notice how the first input has a value="x#y.com", but the second input is still empty. It turns out the second one is empty because it is an input of type password, however I eventually made the form login work.
Here is a list of things you can look into:
have a database enabled in that Spec
have it populated with Users (in case your form validation connects to a DB, that is)
from what I have experienced, using browser.goTo more than once in a test will not work well with form submission (anyone can confirm?)
Hope this helps

form with multiple submit buttons that execute different actions

I'm missing something fundamental when it comes to mapping a view to a controller's action and hoping someone can point me in the right direction. I'm working on an existing project and still familiarizing myself with the language and the way it was configured. I have a form that will resolve a qaCase (question answer case) through the resolveForm action and qaCase/resolve view. below is a simplified version of what I have (please let me know if I need to include more information).
QaCaseController
#RequestMapping(value="/resolve/{id}/**", method=RequestMethod.GET)
public String resolveForm(#PathVariable("id") Integer id, Model model) {
QaCase qaCase = qaCaseDAO.findById(id);
// Load the backing objects into the session
model.addAttribute("qaCase", qaCase);
model.addAttribute("users", userDAO.findAll());
model.addAttribute("exams", examDAO.findAll());
return "qacases/resolve";
}
qaCase/resolve.jsp
the resolve view has a form that will accept text input and a resolve button.
<sf:form method="POST" modelAttribute="qaCase" onsubmit="return isValid()">
// some input fields
<input type="submit" name="submitted" value="resolve" />
</sf:form>
when submit button is clicked, the following query string is created
http://localhost:8080/qacases/resolve/<id>/<location>/<name>/<created by>
What I'd like to do is add an additional input field and button to the existing form so I can optionally add comments instead of resolving a case.
<sf:form method="POST" modelAttribute="qaCase" onsubmit="return isValid()">
// some input fields
<input type="submit" name="submitted" value="resolve" />
</sf:form>
<sf:form method="POST" modelAttribute="qaCase" action="addComment">
// optionally Add comment
<input type="submit" name="submitted" value="addComment" />
</sf:form>
If addComment is clicked then I want the query string to be created.
http://localhost:8080/qacases/addComment
Instead, I get the following query string with a 400 status code.
http://localhost:8080/qacases/resolve/<id>/<location>/<name>/<created by>/addComment
I've been going through configuration files to find how the mapping is being set but haven't had any luck. Not sure if this is an answer that can be answered without someone going through the project and determining how it's configured. Appreciate any advice and/or answers.
when you are using action = "addComment" without "/" before "addComment" in <form> that means you are posting your form to current_url_that_invokes_view/addComment
if add "/" to action = "/addComment" you will go to localhost:8080/addComment
so if you need http://localhost:8080/qacases/addComment
type action = "/qacases/addComment" and pay attantion to "/" before qacases to direct root url

How to pass the exact search result into a new URL

So I'm using a form in a specific page and I want to pass the exact search query to another url after submission
Example: I search for vehicles and I need the result to be domain.com/search/vehicles
So this is what I have so far:
<form id="" action="domain.com/" method="get">
<input type="text" name="search" />
<input type="submit" value="click here" />
</form>
The actual url result here is: domain.com/?search=vehicles
I can't figure out how to make it work
HTML forms will send the inputs in it as GET or POST (or DELETE or PUT also, I think) parameters. So they will send it as url?parameter1=xxx, they won't incorporate them in the url as you want like this url/parameter1/xxx/. I think to do it as you want is easier with jQuery (or plain javascript).
You can do this with jQuery, for example:
$("#form-id").submit(function() {
event.preventDefault(); // stops form from executing normal behavior
var newUrl = // get new url parameters and mount it;
document.location.href = newUrl; // sends to new location
/* Do Something */
return false;
});
document.location.href in javascript will redirect you to a new page.

How to get the filename of a filepicker.io picture to appear in the input field next to it

I am trying to get the filename of an uploaded picture to appear in the input field next to the picker button (for filepicker.io) . Basically I am trying to find what to put in the value field for the input tag to get the filename to appear once the picture is uploaded. Here is the code I have:
<div class="row margin" id='img-row'>
<input id="filename" disabled="disabled" value="<WHAT DO I PUT HERE?>" class="input" type="text" style="width: 360px;"/>
<input name="img" data-fp-class="form-simple-action-btn filepicker_launcher" data-fp-button-text="Choose Image" data-fp-services="COMPUTER,FACEBOOK,FLICKR,INSTAGRAM,PICASA" data-fp-container="modal" data-fp-mimetypes="image/*" type="filepicker" data-fp-apikey="#################" id='campaign-img-input' value="<php echo h($_POST['img'])"/>
</div>
Thank you for your help! I haven't found any other examples like this in the documentation.
The recommended way to do this would be to bind a function to the onchange event of the filepicker input type. Once the upload occurs, the function will be called, and you can pull the filename out of the e.fpfile attribute.
Alternatively, it may be easier to use the filepicker.pick call directly given that you are interested in customizing the behavior. The widget is great for a drop-in solution in many cases, but if you're looking to customize further I'd recommend using the javascript api directly.

How to make a script work for specific form on a page with mulitple forms

I am using a script that disables the submit button unless the user clicks and browses for a file to upload.
$(document).ready(function() {
$('input:submit').attr('disabled',true);
$('input:file').change(
function(){
if ($(this).val()){
$('input:submit').removeAttr('disabled');
}
else {
$('input:submit').attr('disabled',true);
}
});
});
I have 2 forms on the same page and I would like this tied to a specific form.
First form:
<form action="" method="post" name="name1" id="id1">
Second form:
<form action="" method="post" name="name2" id="id2">
is there a way in the javascript to attach it to #id1 specifically and not id2?
would I place it like this? $('#id1.input:submit') - what is the symbol or syntax to add the id into the script? Sorry I'm still learning which parts you can combine.
replace $('input:submit').attr('disabled',true); by
$('form#id1 input:submit').attr('disabled',true);