1. #1
    Ext User cesarulo's Avatar
    Join Date
    Apr 2007
    Posts
    65
    Vote Rating
    0
    cesarulo is on a distinguished road

      0  

    Thumbs up File Upload Nightmare III (featuring Freddy Krueger and Bela Lugosi)

    File Upload Nightmare III (featuring Freddy Krueger and Bela Lugosi)


    Hey people,

    well, everybody on this forum must hate me by now, but I'm unfortunately stuck with one of these real-world problems that need a solution. My apologies about this topic-fixation.

    This is the third in a daisy-chain of fascinating threads, installment number 2 being found here.

    In it, Animal verifies that a form created from no markup with Ext.form.Form will NOT be created with the correct enctype for file uploads even if fileUpload:true is given to its constructor. He then moves on to give me this undocumented fix:

    PHP Code:
    var myForm = new Ext.form.Form({
            
    autoCreate: {
                
    tag'form',
                
    id "upload-form",
                
    method 'POST',
                
    enctype "multipart/form-data"
            
    }
    }); 
    I can almost taste my victory. I give it a shot:

    PHP Code:
    function initFileUploadForm(scope){
        
      var 
    uploadForm = new Ext.form.Form({
         
    //target script of the form action, baseParams will be passed in POST variables
         
    labelAlign'right',
         
    fileUploadtrue,
         
    baseParams: {pathscope.PWD},
         
    autoCreate: {
            
    tag'form',
            
    id "file_upload_form",
            
    method 'POST',
            
    enctype "multipart/form-data"
         
    }
      });
        

      
    //Description textarea
      
    var descriptionField = new Ext.form.TextField({
         
    allowBlankfalse,
         
    id'file_upload_description',
         
    name'description',
         
    fieldLabel'Description',
         
    width300,
         
    blankText'Please type a description'
      
    });
                            
      
    //File upload input
      
    var fileField = new Ext.form.TextField({
         
    allowBlankfalse,
         
    id'upload_file',
         
    inputType'file',
         
    name'upload_file',
         
    fieldLabel'File',
         
    width300,
         
    blankText'Please choose a file'
      
    });
        
      
    //define an UpdateManager so we can control the upload server script's response
      
    scope.fileUploadUpdMgr = new Ext.UpdateManager(Ext.get('file_upload_messages'));

      
    //add fields to form
      
    uploadForm.add(descriptionField);
      
    uploadForm.add(fileField);
      
    uploadForm.addButton({
         
    text'Upload',
         
    handler: function(){
          
    scope.fileUploadUpdMgr.formUpdate('file_upload_form'undefinedtruescope.uploadResponse);
         }
      });
      
    uploadForm.addButton({
         
    text'Cancel',
         
    handler: function(){scope.fileUploadDialog.hide()}
      });
                                
      
    //render it
      
    scope.fileUploadDialog.body.dom.innerHTML '<div id="file_upload_dialog_body"></div>';
      
    uploadForm.render('file_upload_dialog_body');
        
      
    //hook the form up to the GUIContainer that called the function
      
    scope.fileUploadForm uploadForm;

    }
    //function initFileUploadForm(scope)



    function initFileUploadDialog(scope){
      var 
    dlg = new Ext.BasicDialog('file_upload_dialog', {
        
    autoCreatetrue,
        
    height150,
        
    width500,
        
    minHeight100,
        
    minWidth150,
        
    modaltrue,
        
    shadowtrue,
        
    title'File Upload',
        
    syncHeightBeforeShowtrue
      
    });
      
    dlg.addKeyListener(27dlg.hidedlg); // ESC can also close the dialog
      
    scope.fileUploadDialog dlg;
    }
    //function initFileUploadDialog(scope) 
    That's there just in case I'm doing something really stupid and obvious and I'm not seeing it. Anyway, let's cut to the chase: what's my @#^$&@ problem now?!?

    In short: that code aborts with "this.el is not defined" in the following place of ext-all-debug.js, in Ext.extend(Ext.form.Layout, Ext.Component, ...:

    PHP Code:
       onRender : function(ct){
            if(
    this.el){
              
    this.el Ext.get(this.el);
            }else {
              var 
    cfg this.getAutoCreate();
              
    this.el ct.createChild(cfg);
            }
            if(
    this.style){
              
    this.el.applyStyles(this.style);
            }
            if(
    this.labelAlign){
              
    this.el.addClass('x-form-label-'+this.labelAlign); //<--HERE
            
    }
            if(
    this.hideLabels){
              
    this.labelStyle "display:none";
              
    this.elementStyle "padding-left:0;";
            }else{
              if(
    typeof this.labelWidth == 'number'){
                
    this.labelStyle "width:"+this.labelWidth+"px;";
                
    this.elementStyle "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' this.labelPad 5))+'px')+";";
              }
              if(
    this.labelAlign == 'top'){
                
    this.labelStyle "width:auto;";
                
    this.elementStyle "padding-left:0;";
              }
            }
            var 
    stack this.stack;
            var 
    slen stack.length;
            if(
    slen 0){
                if(!
    this.fieldTpl){
                    var 
    = new Ext.Template(
                        
    '<div class="x-form-item {5}">',
                            
    '<label for="{0}" style="{2}">{1}{4}</label>',
                            
    '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
                            
    '</div>',
                        
    '</div><div class="x-form-clear-left"></div>'
                    
    );
                    
    t.disableFormats true;
                    
    t.compile();
                    
    Ext.form.Layout.prototype.fieldTpl t;
                }
                for(var 
    0sleni++) {
                    if(
    stack[i].isFormField){
                        
    this.renderField(stack[i]);
                    }else{
                        
    this.renderComponent(stack[i]);
                    }
                }
            }
            if(
    this.clear){
                
    this.el.createChild({cls:'x-form-clear'});
            }
        }, 
    So I spent about 4-5 hours today stepping through the code and attempting to characterize the situation so I could describe it succinctly in this post. And here goes what I think might be the problem:

    Let's go to Ext.extend(Ext.form.Form, Ext.form.BasicForm,... and get method render() (i.e., find the place in the source where Ext.Form.render() is defined). Here goes its code for clarity's sake:

    PHP Code:
    render : function(ct){
      
    ct Ext.get(ct);
      var 
    this.autoCreate || {
        
    tag'form',
        
    method this.method || 'POST',
        
    id this.id || Ext.id()
      };

      
    this.initEl(ct.createChild(o));

      
    this.root.render(this.el);  //<-- HERE

      
    this.items.each(function(f){
        
    f.render('x-form-el-'+f.id);
      });

      if(
    this.buttons.length 0){
        var 
    tb this.el.createChild({cls:'x-form-btns-ct'cn: {
        
    cls:"x-form-btns x-form-btns-"+this.buttonAlign,
        
    html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
        
    }}, nulltrue);
        var 
    tr tb.getElementsByTagName('tr')[0];
        for(var 
    0len this.buttons.lengthleni++) {
        var 
    this.buttons[i];
        var 
    td document.createElement('td');
        
    td.className 'x-form-btn-td';
        
    b.render(tr.appendChild(td));
        }
      }
      return 
    this;
    }, 
    Notice the line that says "this.root.render(this.el);". If I step OVER it in FireBug, this is where the code breaks. Of course I could go deeper in, but I think this is the line that exposes the conceptual quandary that we're facing.

    1) in this context, this is the Form object that I created, my uploadForm.

    2) this.root is an Ext.form.Layout object. Supposed to be there to act as the container to render the form.

    3) this.el is, I *think* an Ext.Element object wrapping the DOM Element that underlies the form

    4) looking at the docs, Ext.form.Layout.render() (whose call is instantiated by this.root.render(this.el); here) expects an Ext.Element/HTMLElement/String-to-id-an-HTMLElement, and its task is to use that passed argument as a container to render the Form (this) INSIDE IT.

    But... it's getting an Ext.Element not for a container, but for a FORM! I've actually traced the code all the way down to Ext.extend(Ext.form.Layout, Ext.Component,... method onrender():

    PHP Code:
        onRender : function(ct){
            if(
    this.el){
              
    this.el Ext.get(this.el);
            }else {
              var 
    cfg this.getAutoCreate();
              
    this.el ct.createChild(cfg); //<--HERE
            
    }
            if(
    this.style){
              
    this.el.applyStyles(this.style);
            }
            if(
    this.labelAlign){
              
    this.el.addClass('x-form-label-'+this.labelAlign);
            }
            if(
    this.hideLabels){
              
    this.labelStyle "display:none";
              
    this.elementStyle "padding-left:0;";
            }else{
              if(
    typeof this.labelWidth == 'number'){
         .
         .
         .
         . 
    Where, in the line that says

    PHP Code:
    this.el ct.createChild(cfg); 
    this.el is being returned as null because... well, you're asking the DOM to insert a form into another form - ain't gonna happen.

    Now this could be a bug in Ext, or it could be that I'm the who's asking Ext to shove a form into another form. That being said, I've looked at my code until I had to squint, and I can't seem to find the place where I'm saying that. It looks to me like I define a form, add some fields and buttons to it, define a BasicDialog, and then try to put the Form in the Dialog, and render the whole shebang. Plus, if I remove the autoCreate bit Animal gave me and replace it with a simple true value, as it was, well, now it renders, but we're back to the point where it didn't have the proper enctype.

    Well, my apologies once more, but I really need a solution for this and I'm not sure what else to do. Thanks once more Animal for all your help, and if you're gonna be the one on this, I'm sure we'll figure it out sooner or later.

    Best,
    Last edited by cesarulo; 24 May 2007 at 8:18 PM. Reason: minor corrections
    C

  2. #2
    Ext User cesarulo's Avatar
    Join Date
    Apr 2007
    Posts
    65
    Vote Rating
    0
    cesarulo is on a distinguished road

      0  

    Default Sorry, just a note

    Sorry, just a note


    Oh, BTW: I'm on a plane tomorrow, coming back Sunday afternoon MX time. Please don't interpret my lack of answer as lack of interest. I'll be on this as soon as I get back. Cheers
    C

  3. #3
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,496
    Vote Rating
    44
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    Code:
      //render it
      scope.fileUploadDialog.body.dom.innerHTML = '<div id="file_upload_dialog_body"></div>';
      uploadForm.render('file_upload_dialog_body');
    it could be that you're rendering into a nonexistent container. "file_upload_dialog_body" must exist for the form to render properly. innerHTML is asynchronous. Try either sending the file_upload_dialog_body down with page markup, or using DomHelper methods with useDom = true.

    I think you need to step back, take a deep breath and think your plan through. It's not that bad. All the Form constructor+render does is create a form element, and insert it into the DOM inside an element you specify to the render call.

    when you step through render, does "ct" contain a "dom" property that is the div you are expecting?

  4. #4
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,496
    Vote Rating
    44
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    Code:
      scope.fileUploadDialog.body.dom.innerHTML = '<div id="file_upload_dialog_body"></div>';
      uploadForm.render('file_upload_dialog_body');
        
      //hook the form up to the GUIContainer that called the function
      scope.fileUploadForm = uploadForm;
    
    }//function initFileUploadForm(scope)
    
    
    
    function initFileUploadDialog(scope){
      var dlg = new Ext.BasicDialog('file_upload_dialog', {
    So "scope.fileUploadDialog" is the Dialog created by that call to new Ext.BasicDialog???

    Why not just render directly into scope.fileUploadDialog.body.dom instead of poking a DIV into it and rendering the form into that?

    Step back and take a look at what you're doing.

  5. #5
    Ext User
    Join Date
    Apr 2007
    Location
    Sydney
    Posts
    30
    Vote Rating
    0
    Richard is on a distinguished road

      0  

    Default


    Hi,

    Maybe not the best example since it has been coded for my dialog structure, but you should get the meaning.

    The form renders to a div 'tabDialogGeneral' which is a container in my Dialog.

    This posts the file to an ASP.net backend which sends an XML response for success true/false as per the 'XML form sample'.

    Tested on IE7 and FF2.

    Hope this helps

    PHP Code:
    Form = new Ext.form.Form
      
    (
        {
          
    fileUploadtrue,
          
    errorReader: new Ext.form.XmlErrorReader()
        }
      );
        
      
    Form.add
      
    (
          new 
    Ext.form.TextField({fieldLabel:'Image',name:'txtFile',inputType:'file',width:190})
      );

     
    SubmitFunction=function()
      {
     
        
    Form.submit({
            
    url:'your url',
              
    success:function(form,action)
              {
              
    // OK
              
    },                                
              
    failure:function(form,action
              {
              
    // NOT OK
              
    }
          });
         } 
         
     
    SubmitButton=myDialogGeneral.Dialog.addButton('Update'SubmitFunctionmyDialogGeneral.Dialog); 

     
    Form.render('tabDialogGeneral'); 

  6. #6
    Ext User cesarulo's Avatar
    Join Date
    Apr 2007
    Posts
    65
    Vote Rating
    0
    cesarulo is on a distinguished road

      0  

    Tried your changes... problem persists

    Tried your changes... problem persists


    Hey guys,

    first of all thanks for all your answers.

    Second of all my apologies about my persistence. This is getting ridiculous.

    Now, to the reply: Animal, I was doing that div with the innerHTML because I found it in an example somewhere in the forums. I could find the link for you but it doesn't really matter I don't think. In any case, here's what I think are the relevant facts about that way of coding it versus the one you suggest:

    1) if I set the Form constructor's config object autoCreate property to true, it renders, but it's got the wrong enctype and the file doesn't upload.

    2) If I use the autoCreate object you gave me, it seems like the form's enctype is probably correct, but I haven't actually verified this because the form won't even render by virtue of the 'form within a form' stunt that Ext seems to be trying to pull here.

    3) I tried BOTH doing it with that hardcoded div shoved into the innerHTML property AND the way you say, rendering it directly into scope.fileUploadDialog.body.dom. In both cases the same thing happens in case 1) above, and the same is true for case 2). This is, whether I do with the div in the innerHTML or the scope.fileUploadDialog.body.dom seems to have absolutely no effect on the outcome.

    4) Answer to your question:
    when you step through render, does "ct" contain a "dom" property that is the div you are expecting
    is "yes".

    5) I've basically tried four combinations of things here: the 2 possibilities of rendering the form in the innerHTML generated on the fly by me as detailed above vs. using scope.fileUploadDialog.body.dom, in combination with the 2 possibilities of passing fileUpload:true vs. passing fileUpload as the object with 4 properties that Animal suggests in the previous post, in each case with the aforementioned results. ALL OTHER CODE HAS REMAINED UNTOUCHED and has stayed as shown above.


    By virtue of all of the above, I'm more and more convinced that we're looking at some kind of strange bug with Ext here. Think about it: there's only two possible results being

    1) the form doesn't render at all, or
    2) the form renders, but with the wrong enctype and the file doesn't upload.

    This, after I addressed the only possible bug that Animal seems to have found in my code.

    Now, I know the few people who have read this succession of threads and are rolling their eyes and going "oh, boy! here he comes again!" are likely about as bored with this problem as Animal and myself are, and I have no interest in being a difficult user here. I really, really just need to get this application to work, and I care little about theoretical considerations or "the truth" about why this code doesn't work.

    This leaves me with the only possibility of abandoning this approach altogether and trying out a completely different way of solving this problem. For that, Richard, I have to say thanks a lot for that code. It might end up being the scaffolding of a workable solution.

    In the meantime, if you, Animal, or anyone else in the team or among the users can help me sort out why that code (which looks perfect to me and no one has provided hard evidence to the contrary) doesn't work, it would be great. But I'm not really expecting it or planning to spend too much more of my own time figuring it out.

    Just a final suggestion before I throw all that code away: Animal, don't you think this problem's interesting enough to merit Jack's attention, or someone else's in the development team? I think we might be looking at either a big bug in Ext or a big misconception on my part whose elucidation might be very useful to the whole community. But as I say, I will not contend this point. I just need a workable solution of any kind.

    Cheers everybody and thanks so much for your infinite patience, especially Animal.
    C

  7. #7
    Ext User cesarulo's Avatar
    Join Date
    Apr 2007
    Posts
    65
    Vote Rating
    0
    cesarulo is on a distinguished road

      0  

    Default Oh! And I think I know what the bug might be, too.

    Oh! And I think I know what the bug might be, too.


    Hey, sorry; me once more. I have tens of pages of notes I wrote while stepping thru the code and trying to understand what was going on. PLEASE consider this for just a second.

    In
    PHP Code:
    Ext.extend(Ext.form.FormExt.form.BasicForm, {... 
    , function render() (i.e., the definition of BasicForm.render()) goes:

    PHP Code:
        render : function(ct){
            
    ct Ext.get(ct);
            var 
    this.autoCreate || {
                
    tag'form',
                
    method this.method || 'POST',
                
    id this.id || Ext.id()
            };

            
    this.initEl(ct.createChild(o)); //<-- check this out

            
    this.root.render(this.el); //<-- and this

            
    this.items.each(function(f){
                
    f.render('x-form-el-'+f.id);
            });

            if(
    this.buttons.length 0){
                            var 
    tb this.el.createChild({cls:'x-form-btns-ct'cn: {
                    
    cls:"x-form-btns x-form-btns-"+this.buttonAlign,
                    
    html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
                
    }}, nulltrue);
                var 
    tr tb.getElementsByTagName('tr')[0];
                for(var 
    0len this.buttons.lengthleni++) {
                    var 
    this.buttons[i];
                    var 
    td document.createElement('td');
                    
    td.className 'x-form-btn-td';
                    
    b.render(tr.appendChild(td));
                }
            }
            return 
    this;
        }, 

    In the line that goes

    PHP Code:
    this.initEl(ct.createChild(o)); 
    initEl is invoked. Please, please take a look at it. What it does is initialize this.el to Ext.get(ct.createChild(o)). The code right below it calls this.root.render(this.el);, that is, it wants to render this (the form) in this.el (a container of some sort, like a BasicDialog, one would think).

    And yet... ct.createChild(o) is returning the FORM! This code is telling the form (this) to render into itself (this.el). I think the developer who wrote this code made the very understandable mistake of thinking that ct.createChild(o) would return ct (the container - i.e. the DIALOG), when in reality it returns o (the containee - i.e. the FORM).

    Can we have someone from the development team look at this? I think we have a bug...

    Thanks again and my renewed apologies about my relentlessness.

    Best,
    C

  8. #8
    Ext JS Premium Member dj's Avatar
    Join Date
    Mar 2007
    Location
    Germany
    Posts
    573
    Vote Rating
    2
    dj has a spectacular aura about dj has a spectacular aura about dj has a spectacular aura about

      0  

    Default


    it's "this.root.render(this.el);"
    So the code renders the root-container of the Form into the Form. That's not an error. (otherwise, why should it work in the case when it's not a file upload?)

  9. #9
    Ext User cesarulo's Avatar
    Join Date
    Apr 2007
    Posts
    65
    Vote Rating
    0
    cesarulo is on a distinguished road

      0  

    Default


    Quote Originally Posted by dj View Post
    it's "this.root.render(this.el);"
    So the code renders the root-container of the Form into the Form. That's not an error. (otherwise, why should it work in the case when it's not a file upload?)
    Hey dj,

    I have no idea. All I know is that that code is attempting to render a form within a form. I've seen the code as it attempts to do this, have you read my previous posts? If I know my HTML correctly, the children you can append to a form are things like display elements such as tables or divs, and then any input elements you want in the form. NOT other forms.

    Why it tries to insert a form within a form when the enctype is multipart/form-data, and not when it's a regular form, all else being equal, as I clearly indicate above, I have no clue. With finite time and resources, and deadlines on a project for which a simple task is taking me close to a week now, I've only had time to focus on the case that doesn't work, and haven't had much time for the infinity of possible combinations that DO work, but have nothing to do with what I'm trying to do here by virtue of their not constituting solutions to my problem, such as forms that are *not* file uploads.

    Perhaps I'm wrong about my analysis of the possible source of the problem. I've only spent an average of 2-3 hours of each day trying to diagnose it, as I have to spend the rest of the day doing things that will actually help me pay the rent and other such silly things, you know. It was just a suggestion. In any case, what I'm really hoping for is a solution that *helps* me (as in the name of this forum, "Help") get this thing to work. It's a pretty simple problem. I want to render a Form in a BasicDialog. And so far no one has been able to tell me what's wrong with my code.

    So you see, I'm really sorry, but I don't have an answer to your question. Do you happen to know anyone who has an answer to mine?
    C

  10. #10
    Ext JS Premium Member dj's Avatar
    Join Date
    Mar 2007
    Location
    Germany
    Posts
    573
    Vote Rating
    2
    dj has a spectacular aura about dj has a spectacular aura about dj has a spectacular aura about

      0  

    Default


    Quote Originally Posted by cesarulo View Post
    ... I want to render a Form in a BasicDialog. ...
    this BasicDialog doesn't has tabs, does it? If it does the form elements are outside the form, but that's another story...


    Quote Originally Posted by cesarulo View Post
    ... And so far no one has been able to tell me what's wrong with my code.

    So you see, I'm really sorry, but I don't have an answer to your question. Do you happen to know anyone who has an answer to mine?
    Would be good to have a live-demo of your problem where one could step through with firebug and reproduce your error easily.