PDA

View Full Version : Using generated forms with an updateManager for file uploads: no cigar



cesarulo
23 May 2007, 6:23 AM
Hey forum,

I've seen the many postings about problems people have had with file upload forms. It seems to be a rather complicated subject.

I have managed to get a file to upload via a programmatically generated (i.e. no HTML typed) form, and then the problem I have is that I need to handle the server's response to notify the user of the completion status of the operation. Recurring problem as well, it seems.

I've read Animal's assertions in several postings that Ext "scrapes" the answer from the iframe it generates. Unfortunately I'm not sure exactly what is meant by "scrape" in this context, and more importantly, what HAPPENS to the response after it's 'scraped'. I'm also down with the fact that an iframe needs to be created for this, I understand the rationales behind it and everything, no problem there.

If someone helps me solve this problem, I want to hereby formally commit myself to writing a tutorial on how to do this to whittle this problem away once and for all.

I will proceed to give a little bit more detail as to exactly what problems I'm running into. Here it goes:

I have created a function called initFileUploadForm, which goes more or less like this:



function initFileUploadForm(scope){
var uploadForm = /*Ext.get('file_upload_form');*/
new Ext.form.Form({
labelAlign: 'right',
fileUpload: true,
baseParams: {path: scope.PWD}
});

//Description textarea
var descriptionField = new Ext.form.TextField({
allowBlank: false,
id: 'file_upload_description',
name: 'description',
fieldLabel: 'Description',
width: 300,
blankText: 'Please type a description'
});

//File upload input
var fileField = new Ext.form.TextField({
allowBlank: false,
id: 'upload_file',
inputType: 'file',
name: 'upload_file',
fieldLabel: 'File',
width: 300,
blankText: 'Please choose a file'
});

//add fields to form
uploadForm.add(descriptionField);
uploadForm.add(fileField);
uploadForm.addButton({
text: 'Upload',
handler: function(){
Ext.UpdateManager.formUpdate(uploadForm, 'upload.php', true, scope.uploadResponse);
//uploadForm.submit({waitMsg:'Uploading...'});
}
});
uploadForm.addButton({
text: 'Cancel',
handler: function(){scope.fileUploadDialog.hide()}
});

//add repsonse handlers
uploadForm.addListener('actioncomplete', scope.uploadSuccess, scope);
uploadForm.addListener('actionfailed', scope.uploadFailure, scope);

//render it
uploadForm.render(scope.fileUploadDialog.body);

//hook the form up to the GUIContainer that called the function
scope.fileUploadForm = uploadForm;

}//function initFileUploadForm(scope)


The 'scope' argument just points to the GUI Container that contains all the rich client's widgets. An init function there calls this function to generate the form. I should probably make this an instance method, I'll get to it later.

You might notice right off the bat that there's a commented out call to Ext.get('file_upload_form') in the 1st line of the function - I'll get to that later.

Here's the 1st thing I want to get to: Animal clearly indicates in more than one post that using an updateManager's formUpdate() method is the way to gain control over a server's response on file upload. Read up on it, seemed like the right thing. Great! I want one. An updateManager that is.

But no dice. Making a static call to Ext.UpdateManager.formUpdate(uploadForm, blah, blah) directly in the form's upload button handler tells me that



Error: Ext.UpdateManager.formUpdate is not a function
Source File: http://my.local.com/fileshare/javascript/upload.js
Line: 59


when I click the button. Great. Perhaps I need to *instantiate* an UpdateManager 1st, and then I get to call formUpdate on it. Let's try that. Let's create one. The UpdateManager constructor's interface:


public function UpdateManager(String/HTMLElement/Ext.Element el, [Boolean forceNew])

Awesome. It wants an HTML Element. Makes sense... and you might have noticed that I'm rendering my uploadForm in a mysterious scope.fileUploadDialog. Here's the code that creates it:



function initFileUploadDialog(scope){
var dlg = new Ext.BasicDialog('file_upload_dialog', {
autoCreate: true,
height: 150,
width: 500,
minHeight: 100,
minWidth: 150,
modal: true,
shadow: true,
title: 'File Upload',
syncHeightBeforeShow: true
});
dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
scope.fileUploadDialog = dlg;
}//function initFileUploadDialog(scope)


Just a BasicDialog. But it's there. So let's try giving IT as an argument to the UpdateManager constructor: insert


scope.um = new Ext.UpdateManager(scope.fileUploadDialog);

right before the '//add fields to form' comment in initFileUploadForm() above, and also replace the static call


Ext.UpdateManager.formUpdate(uploadForm, 'upload.php', true, scope.uploadResponse);

in the form upload button handler with


scope.um.formUpdate(uploadForm, 'upload.php', true, scope.uploadResponse);

Let's try it! This HAS to work! YES! Uhhhh... no :(

This time the form doesn't even appear, and I get



el has no properties
http://my.local.com/fileshare/javascript/ext-1.0/ext-all-debug.js
Line 4663


Inspection with firebug reveals that it's the call to the UpdateManager constructor that's breaking it. It doesn't like my BasicDialog as an argument, apparently it doesn't qualify as an HTML element.

I've explored a few other avenues, such as attempting to get an updateManager from the uploadForm or the uploadDialog with .getUpdateManager(), to no avail. They're not Ext.Elements, and I don't know how to get one from them. After all, I need to show the things on the screen, I need to update their contents, and JavaScript is all about duck typing... I just need to have those things behave like an HTMLElement or an Ext.Element so I can hook an updateManager up to them....

I know I said I would explore the possibility of using an HTML-type form in the document with the commented-out line


Ext.get('file_upload_form');

but this post is getting too long already and here's the skinny on that: it doesn't work either. If someone thinks it's a path worth exploring from what I've typed so far, then we can delve into it.

Help please?!? I think this is an issue whose clarification might help very many people.

Thanks in advance. The product is unparalleled and the response times in the forum are outstanding. Let's keep working to put Ext at the very top of the AJAX toolkit market!

Cheers,

cesarulo
23 May 2007, 7:57 AM
Hey guys,

okay, I've tried a variation on the code I post above, here's it in point form:

1) make an HTML div for the BasicDialog that contains the file upload form instead of using autoCreate: true,

2) get an updateManager from that BasicDialog now that it's got a true DOM Element underlying it,

3) Create the form programmatically as I'm doing in the example above, no change here

4) render the form in the BasicDialog, no change here either.


It flies a little bit further like this, and I *can* (yay!) get the updateManager from it. Problem arises when I actually issue the call to formUpdate:



formUpdate : function(form, url, reset, callback){
if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
formEl = Ext.getDom(form);
if(typeof url == "function"){
url = url.call(this);
}
if(typeof params == "function"){
params = params();
}
url = url || formEl.action;
var cb = {
success: this.successDelegate,
failure: this.failureDelegate,
timeout: (this.timeout*1000),
argument: {"url": url, "form": formEl, "callback": callback, "reset": reset}
};
var isUpload = false;
var enctype = formEl.getAttribute("enctype");
if(enctype && enctype.toLowerCase() == "multipart/form-data"){
isUpload = true;
cb.upload = this.successDelegate;
}
this.transaction = Ext.lib.Ajax.formRequest(formEl, url, cb, null, isUpload, this.sslBlankUrl);
this.showLoading.defer(1, this);
}
},


First off, what is this



if(typeof params == "function"){
params = params();
}


'params'? There's no params, unless you're looking for a global object (which would be very surprising given how much care you've put into writing this in OOP). It's not an argument, and it's not prefixed by 'this', so that cuts it. That var will never be set to anything other than 'undefined', short of garbage from the global space. Dangerous. Am I missing something?

In any case, that snippet should be completely inconsequential as long as there's nothing there, and in my app, there isn't. On with it.

The function halts at



var enctype = formEl.getAttribute("enctype");


turns out getAttribute() is not a function, even though I created it from an HTMLElement, and the signature for formUpdate reads


public function formUpdate(String/HTMLElement form, [String url], [Boolean reset], [Function callback])

So I decided to grep for getAttribute in the whole Ext distro tree. Only found it defined in yui-utilities.js, which is minified so I can't read it, and in source/yui/amination.js, which I haven't even read yet, but... animation?!!? Huh? I thought I was trying to upload a file here, why is a method that's only defined in a class for moving pretty pictures around being called from my file-uploading code? To say that I'm very confused is probably the understatement of the year :D

So: how do I get a relevant getAttribute() method in that thing? Duck typing, I can just inject it, but... where do I get it from, and why is it not there to begin with? My fear is that either:

1) Everything I know is wrong, or
2) There's a fundamental flaw in this part of Ext.

Please tell me I'm wrong on both accounts.

Best,

Animal
23 May 2007, 8:23 AM
There's a bug in formUpdate. "params" should be a parameter to formUpdate IMHO, so that you can add some extra parameters to the form submission.

As for getAttribute not being a function. It is if formEl is being picked up correctly. When you stop in Firebug at that line, what does formEl contain?

Animal
23 May 2007, 8:25 AM
http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html

search for "getAttribute"

cesarulo
23 May 2007, 9:51 AM
Hey Animal,

thanks for your reply. About the bug, should I file a bug report, or are you guys on it? I'm not sure how you guys handle these things.

About the contents of formEl, that question gets difficult to answer when don't have classing proper :) But here go some snapshots from the FireBug DOM explorer.

I hope the property names ring a bell. Particularly intersting is the 'el' property of formEl, which I also expanded for you. It seems to have the getAttributeNS() method but not plain ole' getAttribute...

So I guess at this point we have 2 conjectures:

1) the problem's in Ext.getDom(), or
2) the problem's in the form argument of the function, it's not of the correct nature, and that's what's causing getDom() to return getAttribute()-deprived gibberish.

Am I following?

Looking forward to your reply. Thanks again,

Animal
23 May 2007, 10:01 AM
File a bug report about the params thing.

formEl should be a DOM form HtmlElement, NOT an Ext.form.Form in UpdateManager.formUpdate.

Don't pass an Ext.form.Form in! Read the docs as to what type that param is. Pass an Html form element.

http://www.extjs.com/deploy/ext/docs/output/Ext.UpdateManager.html#formUpdate

cesarulo
23 May 2007, 10:26 AM
I hope I'm not getting on your nerves, Animal. I'm sorry about my ignorance, but I'm not too sure how to obtain an HTML Element from a form that was generated programmatically by using the Form class. I guess it probably has some obscure property buried deep within it if I look with Firebug, but I wanted to check with the forum 1st in case there's a 'cleaner' way to do it. The problem with using undocumented private properties is, among others, that compatibility with future versions is not guaranteed. There is no getHTMLElem() public function in the Form class API, so I'm not too sure what to do. If there's something obvious I'm not seeing, can you slap me out of my drowse? Just put me out of my misery, public humiliation is a fair price to pay for your advise (just kidding).

Once I find out how to get that DOM element that will be it and I'll leave you alone. Thanks in advance once more,

Animal
23 May 2007, 10:44 AM
No, not at all, I can see that you're getting in and battling with it!

I don't think there's a public way to get the DOM form from an Ext form. IMHO, there should be. There is the private "el" property, but best not use that.

But as the formUpdate docs say, you can pass a string being the id of the form, so use the id that you gave the form.

cesarulo
23 May 2007, 10:50 AM
Wicked! I'll try that.. if I'm not seeing a config option to set the Form's id, I guess I'll have to put some research into that too.

In the meantime, is there a way I can suggest the inclusion of a public method/property to get the DOM element out of the form? I've already filed a bug report for the params problem, would this go in the bugs forum too?

Thanks man! You're coming right through, you're a sport.