2
votes

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.

3

3 Answers

2
votes

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.

1
votes

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

0
votes

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.