Step 2: Hidden iframe for ajax-like file upload
In this step we upload the file in the background in an AJAX-like style. Note we are not using the AJAX XMLHttpRequest
as at the time of writing it was not possible to use an XMLHttpRequest
for multipart form uploads. The work-around is to use a hidden iframe to submit the form. The main render method becomes:
renderContentOn: html
html div
class: 'fileuploadExample';
with: [
self renderUploadFormOn: html.
self renderHiddenIFrameOn: html.
self renderUploadedFilesOn: html.
self renderFileUploadedCallbackJSOn: html ]
the iframe is rendered off-screen (position:absolute;top:-1000px;left:-1000px
)
renderHiddenIFrameOn: html
html iframe
name: 'hiddenImageIFrameUploader';
id: #hiddenImageIFrameUploader;
style: 'position:absolute;top:-1000px;left:-1000px';
attributeAt: 'onload' put: 'fileUploadedCallback()'
the form declaration is changed to set the form’s target to the iframe:
html form
multipart;
attributeAt: 'target' put: 'hiddenImageIFrameUploader';
class: 'uploadForm'; with: [ "form fields go here..." ]
Now when the form’s submit button is pressed the response is rendered within the iframe.
The response handling in the hiddenInput
completes with a call to renderIFrameResponse
as:
html hiddenInput
callback: [:val | | uploadFieldName |
"what name did Seaside assign to the file upload form field?"
uploadFieldName := fileUploadField attributeAt: 'name'.
self storeUploadedFile: uploadFieldName.
self renderIFrameResponse ].
with renderIFrameResponse
rendering the uploaded files:
renderIFrameResponse
"Respond directly with the uploaded files"
self requestContext respond:
[ :response |
response
contentType: WAMimeType textHtml;
nextPutAll: (WARenderCanvas builder
fullDocument: true;
scriptGeneratorClass: JQScriptGenerator; "removes the onload from body"
render: [:html | self renderUploadedFilesOn: html ] )]
The iframe declaration above contains attributeAt: 'onload' put: 'fileUploadedCallback()'
. When the response is loaded by the iframe, the iframe’s onload
will trigger a call to the javascript method fileUploadedCallback()
, which is defined by:
renderFileUploadedCallbackJSOn: html
html script:
'var fileUploadedCallback = function() {
$("#uploadedFilesContainer").replaceWith($("#hiddenImageIFrameUploader").contents().find("#uploadedFilesContainer"))
}'
This script copies a div (#uploadedFilesContainer
) inside the just updated iframe to the same named div, rendered just below the upload form, displaying the newly uploaded file and any previously uploaded files.
Download the code
The code described above is contained in NAFileUploadStep2
and can be downloaded from the repository http://www.squeaksource.com/fileupload.