Sunday, March 24, 2013

Debugging Your ColdFusion Code When Using ExtJS

It can be a chore to determine what is going wrong with your application when using ExtJS.

For starters, you are expecting the nice debugging errors you get out of ColdFusion. You do not get that.

Instead you get a blank screen and so you know whatever you changed no longer works.

So what do you do?

First, make sure you are testing with Firefox and have installed Firebug.

Then turn on Firebug.

Select console and select errors. This should tell you what the problem is and it will give you the line number that the bug relates to.

Usually I unsave my last group of changes and it works again! But, now you know what line of functionality to investigate.

The problem could be your call to your form submit.cfm or your cfc.

If you want to know what is happening with your calls go to the net tab. Select xhr and it will show you whether you made any calls to your cfc. If so, look at the post and see what is being sent to your application. Is this what you expected. If there is an error in your code and CF returns back error messages you will see them in the HTML tag.

You may need to dump some of your variables. You are not going to see the cfc page so you need to dump those variables to a page. This is how you do that:


 



I hope this provides some help. You are not alone. See my post about determining what to pass back to ExtJS. It may also provide some help.

Friday, March 22, 2013

How to format your html for syntax highlighting

This is mainly a post for myself. Paste your code into this page and it will convert your html. You can then copy it and post it into blogger. http://formatmysourcecode.blogspot.com/

ColdFusion Loading and Saving The Coolest ExjtJS Form

I read through the ExtJS Cookbook by Jorge Ramon and he presented the coolest form I have seen. But, I needed a way to populate the form with ColdFusion. Well I checked the Sencha blogs and found a cool example by Murray Hopkins. Here is the link to the discussion: http://www.sencha.com/forum/showthread.php?20172-Example-FormPanel-using-JSON-to-load-and-submit-with-Coldfusion-or-PHP

I worked through the example where he provides a window to enter a record number. I had just been experimenting with linked combo boxes and decided to use a seperate search form using linked combo boxes to drive or populate the form. The result looks like this (click on the picture and you will get a better view):

Here is the picture of the form with the picture upload:

I think this is wonderful. It even includes an upload window to upload a new picture.

Sweet.So how do we do it? Well first I will give you the entire code base and then I will explain it.

HTML Files are here:
<!DOCTYPE HTML>
<html>
<head>
    <title></title>
   
    <link rel="stylesheet" type="text/css" href="resources/css/ext-all.css">
    <style type="text/css">

        .x-fieldset
        {
            border-style: solid none none none;
            border-width: 1px;
            border-color: #B5B8C8;
            padding: 10px;
            margin-bottom: 10px;
            margin-right: 20px;
            display: block;
        }
        .left-right-buttons .x-panel-btns {
          text-align: 'center'
        }
        .left-right-buttons .x-panel-btns table {
            width: 100%;
        }
        .left-right-buttons .x-panel-btns .x-btn-left {
            float: left;
        }
    </style>

  <script src="adapter/ext/ext-base.js"></script>
        <script src="js/ext-all-debug.js"></script>   

    <script type="text/javascript">

        // Loading form data from the server.

        Ext.BLANK_IMAGE_URL = 'images/default/s.gif';
        // Required if showing validation messages
        Ext.QuickTips.init();
        
    
    var picBox1 = {
    columnWidth: '100 px',
    bodyStyle: 'padding:10px',
    items: [
        { xtype: 'box',
            autoEl: { tag: 'div',
            html: '<img id="mypic" src="img/" + Ext.BLANK_IMAGE_URL + class="img-contact" width="100" height="150"/>'
            }
        },
        {xtype: 'textfield',
         name: 'pic2',
         id:     'pic2',
         label: 'File Name'
         }
    ]
}

var picFiles = { 
    columnWidth: .65,
    layout: 'form',
    labelAlign:'top',
    items: [
      //  {
//            xtype: 'textfield',
//            fieldLabel: 'Current',
//            labelSeparator: '',
//            name: 'currPic',
//            id:'currPic',
//            readOnly: true,
//            disabled:true,
//            anchor:'100%'
//        },
        {
            xtype: 'textfield',
            fieldLabel: 'New (JPG or PNG only)',
            labelSeparator: '',
            name: 'newPic',
            id:'newPic',
            style:'width: 300px',
            inputType: 'file',
            allowBlank: false
        }
    ]
}
/* get the contact id from the contact form and store it in an itermediary value*/
 var myholdid = Ext.getCmp('id');
 var  pictUploadForm = new Ext.FormPanel({
        id: 'pictUploadForm',
        frame: true,
        title: 'Change Picture For',
        bodyStyle: 'padding:5px',
        width: 620,
        height: 300,
        layout: 'column',
        /*url: 'contact-picture.aspx',*/
        /*url: 'fileuploadtest.cfm',*/
        /*url: 'fileuploadaction.cfm',*/
        url: 'fileuploadaction2.cfc?method=uploadPic',
        params: {
            action: 'upload',
            /* pass the itermediary value to the cfc so the pic can be updated*/
            id: 'contactid' 
            },
        method: 'POST',
        fileUpload: true,
        enctype: 'multipart/form-data',
        /*width: 900,
        height: 600,*/
        items: [picBox1, picFiles]
        });     
      

var LastNameStore = new Ext.data.JsonStore({
    url: 'contacts.cfc?method=getLastName',
    baseParams:{cmd:'LastName'},
    root: 'ROWS',
    fields: [ 'LASTNAME']
});

var FirstNameStore = new Ext.data.JsonStore({
    url: 'contacts.cfc?method=getFirstName',
    baseParams: { cmd:'FirstName'},
    root: 'ROWS',
     fields: ['FIRSTNAME', 'RECORDID' ]
});

var LastNameCombo = {
    xtype: 'combo',
    id: 'LastNameCombo',
    store: LastNameStore,
    displayField: 'LASTNAME',
    valueField: 'LASTNAME',
    typeAhead: true,
    editable:false,
    mode: 'remote',
    forceSelection: true,
    triggerAction: 'all',
    fieldLabel: 'LastName',
    emptyText: 'Select a name...',
    selectOnFocus: true,
    anchor:'95%',
    listeners: {
        'select': function(cmb, rec, idx) {
            FirstNameCbx = Ext.getCmp('FirstName-combo');
            FirstNameCbx.clearValue();
            FirstNameCbx.store.load({
                params: { 'LastNameCombo': this.getValue() }
            });
            FirstNameCbx.enable();                   
        }
    }
}

var FirstNameCombo = {
    xtype: 'combo',
    id:'FirstName-combo',
    store: FirstNameStore,
    displayField: 'FIRSTNAME',
    valueField: 'RECORDID',
    typeAhead: true,
    editable: false,
    mode: 'local',
    forceSelection: true,
    triggerAction: 'all',
    fieldLabel: 'FirstName',
    emptyText: 'Select a name...',
    selectOnFocus: true,
    disabled: true,
    anchor: '95%',
    listeners: {
        'select': function(cmb, rec, idx) {
            LastNameCombo = Ext.getCmp('LastName-combo');
        
         Ext.getCmp('contactForm').getForm().load({ url: 'contactform1.cfc?method=getContacts', params:{'RECORDID': this.getValue() },waitMsg: 'Loading'});
                      
        }
    }
}

var nameAndCompany = { columnWidth: .5,
    layout: 'form',
    bodyStyle:'padding-top:1px',
    items: [
        { xtype: 'textfield',
            fieldLabel: 'First Name',
            name: 'firstname',
            anchor: '95%'
        }, {
            xtype: 'textfield',
            fieldLabel: 'Last Name',
            name: 'lastname',
            anchor: '95%'
        }, {
            xtype: 'textfield',
            fieldLabel: 'Company',
            name: 'company',
            anchor: '95%'
        }, {
            xtype: 'textfield',
            fieldLabel: 'Title',
            name: 'title',    
            anchor: '95%'
        },
         {
            xtype: 'textfield',
            fieldLabel: 'ID',
            name: 'id',
            id: 'contactid',    
            anchor: '95%'
        }
    ]
}

var picBox = {
    columnWidth: .5,
    bodyStyle: 'padding:0px 0px 0px 40px',
    items: [
        { xtype: 'box',
            autoEl: { tag: 'div', style: 'padding-bottom:20px',
        
            html:  '<img id="pic1" src="img/ + contact.pic+" width="100" height="150" />'
                
            }
        },    
        {xtype: 'textfield',
        name: 'pic',
        id: 'pic',
        label: 'File Name' 
        
        },
        { xtype: 'button', 
        text: 'Change Picture',
        handler: function(){
             Ext.getDom('pic1').src = "img/" + Ext.getDom('pic').value;
              /*attempting to load the picture */
            
              var mywin = new Ext.Window({
                    cls: 'left-right-buttons'
                  ,title: 'Change Picture For'
            /*      ,id            : 'mywin'*/
                  ,width        : 650
                  ,height        : 325
                  ,plain        : true
                  ,modal        : true
                  ,closeAction    : 'hide' 
                  ,closeable    :  true
                  ,items: [
                      pictUploadForm
        ],
                buttons: [{
            text: 'Close',
            name: 'Closebtn',
            id: 'Closebtn',
            handler: function(){
                mywin.hide();
        }
                            },
        {
            text: 'Upload Picture',
            handler: function() {

            var theForm = pictUploadForm.getForm();
                
                if (!theForm.isValid()) {
                    Ext.MessageBox.alert('Change Picture', 'Please select a picture');
                    return;
                }
                if (!validateFileExtension(Ext.getDom('newPic').value)) {
                    Ext.MessageBox.alert('Change Picture', 'Only JPG or PNG, please.');
                    return;
                }
                theForm.submit({
                    params: { act: 'setPicture', 'RECORDID': (Ext.getDom('RECORDID'.value)) }
                    ,
                    waitMsg: 'Uploading picture', 
                    handler: function() {
                     Ext.MessageBox.alert('Message', 'hello Rick');
                 }
                })
            }
        },
        {xtype:'tbfill'},
         {
             text: 'Cancel',
            handler: function(){
                pictUploadForm.getForm().reset();
         },
    
            
            handler: function(){
                
                Ext.getDom('mypic').src =  pic1 ;
                Ext.getDom('mypic').src = Ext.get('pic');
                Ext.getDom('pic2').value = Ext.getDom('pic').src;
            
                mywin.hide();
            }
        }],
        buttonAlign:'center'
    });

                  
              mywin.show();
            
                     Ext.getDom('pic2').value = Ext.getDom('pic').value;
                  /*  Ext.getDom('currPic').value = Ext.getDom('pic').value;*/
                          Ext.getDom('currPic').value = Ext.getDom('pic').src;
                  Ext.getDom('mypic').src = Ext.getDom('pic1').src;
                 alert('Getting Ready for Picture Upload');        
                        }
}
]
}
;
              



var internet = { columnWidth: .5,
    layout: 'form',
    items: [{ xtype: 'fieldset',
        title: 'Internet',
        autoHeight: true,
        defaultType: 'textfield',
        items: [{
            fieldLabel: 'Email',
            name: 'email',
            vtype: 'email',
            anchor: '95%'
        }, {
            fieldLabel: 'Web page',
            name: 'webpage',
            vtype: 'url',
            anchor: '95%'
        }, {
            fieldLabel: 'IM',
            name: 'imaddress',
            anchor: '95%'
        }]
    }]
}

var phones = { columnWidth: .5,
    layout: 'form',
    items: [{ xtype: 'fieldset',
        title: 'Phone Numbers',
        autoHeight: true,
        defaultType: 'textfield',
        items: [{
            fieldLabel: 'Home',
            name: 'homephone',
            anchor: '95%'
        }, {
            fieldLabel: 'Business',
            name: 'busphone',
            anchor: '95%'
        }, {
            fieldLabel: 'Mobile',
            name: 'mobphone',
            anchor: '95%'
        }, {
            fieldLabel: 'Fax',
            name: 'fax',
            anchor: '95%'
        }]
    }]
}

var busaddress = { columnWidth: .5,
    layout: 'form',
    labelAlign: 'top',
    defaultType: 'textarea',
    items: [{
        fieldLabel: 'Business',
        labelSeparator:'',
        name: 'baddress',
        anchor: '95%'
    }
    ]
}

var homeaddress = { columnWidth: .5,
    layout: 'form',
    labelAlign: 'top',
    defaultType: 'textarea',
    items: [{
        fieldLabel: 'Home',
        labelSeparator:'',
        name: 'mailingaddress',
        id: 'mailingaddress',
        anchor: '95%'
    }
    
     ]
}
var mailtostuff = { columnWidth: 1,
    layout: 'form',
    labelAlign: 'top',
    defaultType: "radiogroup",
    items: [{
          fieldLabel: "Mail To",
            id: "mailto",
            defaults: {xtype: "radio",name: "mailto"},
            items: [
                {
                    boxLabel: "Business",
                    inputValue: "Business",
                },
                {
                    boxLabel: "Home",
                    inputValue: "Home",
                }
              ]
    }
    ]
}
function hideWindow()
{ 
    Ext.getCmp('mywin').hide(); 
    //Even this hide methof has got same three optional parameters like animateTarget, callback, Object scope
    //getCmp method is to retrieve the reference to the component
}
        
function validateFileExtension(fileName) {

    var exp = /^.*\.(jpg|JPG|png|PNG)$/;
    return exp.test(fileName);
    
    
}

Ext.onReady(function() {
    
    
    

    var searchForm = new Ext.FormPanel({
        frame: true,
        title: 'Find Contacts',
        bodyStyle: 'padding:5px',
        width: 650,
        id: 'name-search-frm',     
        url: 'get-contacts.cfc',
        items: [
           LastNameCombo, FirstNameCombo
        ],
        buttons: [{
            text: 'Go',
            region: 'center',
            /*buttonAlign:'center',*/
            handler: function() {
                Ext.getCmp('searchForm').getForm().submit();
            }
        }, 
          {xtype:'tbfill'},
          {xtype:'tbfill'},
          {xtype:'tbfill'}, {
            text: 'Cancel',
            handler: function() {
                win.close();
                searchForm.getForm().reset();
            }    
        }],
        buttonAlign:'right'
    });
    
    
    
   
     contactForm = new Ext.FormPanel({
        id: 'contactForm',
        frame: true,
        title: 'Contact Form',
        bodyStyle: 'padding:5px',
        width: 650,               
        items: [{
            bodyStyle: {
                margin: '0px 0px 15px 0px'
            },
            items: [{
               layout: 'column',
               items: [nameAndCompany, picBox]
            }]
            }, {
               items: [{
                   layout: 'column',
                   items: [phones, internet]
                }]
                }, {
                   xtype: 'fieldset',
                   title: 'Addresses',
                   autoHeight: true,
                   hideBorders: true,
                   layout: 'column',
                   items: [busaddress, homeaddress, mailtostuff]
        }],
        buttons: [{
            text: 'Save',
            disabled: false,
            handler: function(){
            contactForm.form.submit({
                url: 'json-form-load-server-combo10-submit.cfm', // Coldfusion
                 //url:'json-form-submit.php', // PHP
                //url:'json-form-submit.asp', // ASP
                waitMsg: 'Saving Data...'
                /*  Alternatively, instead of using actionfailed / complete (below) you could use these functions:    */
                ,
                success: function (form, action) {
                    Ext.MessageBox.alert('Message', 'Saved OK 3');
                    /*mywin.hide();*/
                },
                failure:function(form, action) {
                    Ext.MessageBox.alert('Message', 'Save failed');
                }
                
           });
         }
    //}];
        }, {xtype:'tbfill'},{
            text: 'Cancel',
            buttonAlign:'right',
            handler: function() {
                    contactForm.getForm().reset();
            }
        }],
        buttonAlign:'right'
    });


contactForm.on({
    actioncomplete: function(form, action){
        if(action.type == 'load'){
            var contact = action.result.data;
            contactForm.setTitle(contact.firstname + ' ' + contact.lastname);
            Ext.getDom('pic1').src = "img/" + contact.pic ;
            
              pictUploadForm.setTitle(contact.firstname + ' ' + contact.lastname);
              
            
            mypic=document.getElementById(pic1);
            
        }
    }
});

searchForm.render(document.body);

contactForm.render(document.body);


contactForm.getForm().load({ url: 'contactform.cfc?method=getContacts', params:{id:'contact1'},waitMsg: 'Loading'});

 // Define the Submit button and the action required. This will be enabled if the Load is successful.
    var submit = contactForm.addButton({
        text: 'Submit',
        disabled: true,
        handler: function(){
            contactForm.form.submit({
                url:'json-form-submit.cfm', // Coldfusion
            
                waitMsg:'Saving Data...'
               
            });
         }
    });

var addpic = contactForm.addButton({
        text: 'addpic',
        disabled: false,
        handler: function(){
            contactForm.form.submit({
                url:'form-file-upload.cfm', // Coldfusion
                
                waitMsg:'Saving Data...'
                
            });
         }
    });
    contactForm.render('form-ct');
    
    
pictUploadForm.on({
    actioncomplete: function(form, action) {
          if(action.type == 'load'){
                submit.enable();
                 Ext.getDom('pic2').val = Ext.getDom('pic').val;
                  Ext.get('mypic').dom.src = Ext.get('pic1').dom.src;
                /*  Ext.get(id).dom.src = 'newsrc.jpg'*/
                Ext.MessageBox.alert('pictUploadForm.actioncomplete', 'upload ok.'); // Optional. Just for testing
            } 
        
        
        var result = Ext.decode(action.response.responseText);
        

        
            
        /*Ext.MessageBox.alert('Picture Upload Successful', + file );*/
          var responseObj = Ext.decode(action.response.responseText);
          if (responseObj.success){ 
              var file = responseObj.data.file; } 
               /*Ext.getDom('pic1').src = 'img/'+file+'"'+ ' width="100" height="150"';*/
               Ext.getDom('pic1').src = 'img/'+file;
                Ext.getDom('mypic').src = 'img/'+file;
               Ext.getDom('currPic').value = file;
               Ext.getDom('pic2').value = file;
              Ext.getDom('pic').value = file;
                
               console.log('file name', file);
        
               Ext.MessageBox.alert('Picture was successfully uploaded!', + responseObj.data.file );
              
               
            
               Ext.getCmp("Closebtn").handler.call(Ext.getCmp("Closebtn").scope);
             
               Ext.Msg.show({
title: 'Picture Upload',
msg: 'Are you finished?',
buttons: {
yes: true,
no: true,
cancel: true
}
});
               
          if (responseObj,!!success){
               Ext.MessageBox.alert('file upload failed' ); 
          }

    }
});


    contactForm.on({
        actioncomplete: function(form, action){
                            // Only enable the submit button if the load worked
            if(action.type == 'load'){
                submit.enable();
                Ext.MessageBox.alert('contactForm.actioncomplete', 'All OK 1.'); // Optional. Just for testing
            } 
            
            if(action.type == 'submit'){
                        // If the responseText is a null string, Ext doesnt raise an error so trap it here 
                        // as an error because it should be some json.
                          Ext.getDom('pic').src = pic.file;
                    Ext.getDom('currPic').src = pic.file;
                    console.log('pic');
                if(action.response.responseText == '') {
                    Ext.MessageBox.alert('contactForm.actioncomplete error', 'Form submit returned an empty string instead of json');
                } else Ext.MessageBox.alert('contactForm.actioncomplete', 'All OK 2.'); // Optional. Just for testing
            }
        },
                       
                        // NOTE: the line var result = Ext.decode(action.response.responseText); must be
                        // called AFTER you have made sure there wasnt a failureType == "connect" or you 
                        // will probably get a JS error in the Ext library.
                            
        actionfailed: function(form,action){
            //alert('actionfailed');            
            if(action.type == 'load') { // Handle the LOAD errors
                if (action.failureType == "connect") { 
                    Ext.MessageBox.alert('contactForm.actionfailed error', 'Form load failed. Could not connect to server.');
                } else {
                    if (action.response.responseText != '') {
                        var result = Ext.decode(action.response.responseText);
                        if(result && result.msg) {            
                            Ext.MessageBox.alert('contactForm.actionfailed error', 'Form load failed with error: ' + action.result.msg);
                        } else {
                            Ext.MessageBox.alert('contactForm.actionfailed error', 'Form load failed with unknown error (possibly missing the "success" field in the json). Action type='+action.type+', failure type='+action.failureType);
                          }
                    } else {
                        Ext.MessageBox.alert('contactForm.actionfailed error', 'Form load returned an empty string instead of json');
                      }
                  }
            } 

            if(action.type == 'submit') { // Handle the SUBMIT errors
                
                if (action.failureType == "connect") { 
                    Ext.MessageBox.alert('contactForm.actionfailed error', 'Form submit failed. Could not connect to server.');
                } else 
                    if (action.failureType == "server") { 
                        // These arent "errors" as such, they are validation issues trapped by the server script and passed back for the user to correct
                    } else {    
                        var result = Ext.decode(action.response.responseText); 
                        if(result && result.msg) {            
                            Ext.MessageBox.alert('contactForm.actionfailed error', 'Form submit failed with error: ' + action.result.msg);
                        } else {
                            Ext.MessageBox.alert('actionfailed Error', 'Form submit returned unknown error. Action type='+action.type+', failure type='+action.failureType);
                          }
                      }
            }   
            
        } // end actionfailed listener
    }); // end contactForm.on

//});



});
   
    </script>
</head>
<body style="padding: 20px">
 <div id="yourwin" class="x-hidden" style="padding:10px">

        <input type="button" value="Close" onClick="hideWindow();"/>
<br/>
</div>
</body>
</html>
Here is the code for the CFC:





        
             
        
         
        
        
        
         
        
          SELECT id 
               , firstName 
               , lastName
               , company
               , title
               , pic
               , email
               , webPage
               , imaddress
               , homePhone
               , busPhone
               , mobPhone
               , fax
               , mailto
               , bAddress
               , hAddress
               , mailingAddress
            FROM contactform
            
            where id = #form.RECORDID#
            
            where id = 1
            
         
        
        
       
        
            thestruct = StructNew(); 
   rtnStruct = StructNew();
   thestruct['success'] = StructNew(); 
   thestruct['success'] = true; 
            thestruct['data'] = StructNew();
            thestruct['data']['id'] = getContacts.id;
            thestruct['data']['company'] = Replace(getContacts.company, chr(13) & chr(10), ' ','ALL');
   thestruct['data']['title'] = getContacts.title;
   thestruct['data']['firstname'] = getContacts.firstName;
            thestruct['data']['lastname'] = getContacts.lastName;
   thestruct['data']['pic'] = Replace(getContacts.pic, '\', '','ALL');
   thestruct['data']['email'] = Replace(getContacts.email, chr(13) & chr(10), ' ','ALL');
            thestruct['data']['webpage'] = getContacts.webPage;
   thestruct['data']['imaddress'] = Replace(getContacts.imAddress, chr(13) & chr(10), ' ','ALL');
   thestruct['data']['homephone'] = Replace(getContacts.homePhone, chr(13) & chr(10), ' ','ALL');
   thestruct['data']['busphone'] = Replace(getContacts.busPhone, chr(13) & chr(10), ' ','ALL');
   thestruct['data']['mobphone'] = Replace(getContacts.mobPhone, chr(13) & chr(10), ' ','ALL');
   thestruct['data']['fax'] = Replace(getContacts.fax, chr(13) & chr(10), ' ','ALL');
   thestruct['data']['mailto']= getContacts.mailto;
   thestruct['data']['baddress'] = getContacts.baddress;
   thestruct['data']['haddress'] = getContacts.haddress;
   thestruct['data']['mailingaddress'] = Replace(getContacts.mailingAddress, chr(13) & chr(10), ' ','ALL');
        
  return serializejson(thestruct);
  rtnStruct.success = true;
  rtnStruct.data = getContacts;
  return serializeJSON(rtnStruct,false);
        
        
        

 
    
    
 
        
        
        
  
        
        

   
               SELECT id as RECORDID, first as FIRSTNAME
                  FROM  contactform
                  where id = #arguments.RECORDID#
            
            
           
     
                //format the query for ExtJS json
                compObj = CreateObject("component", "cf_extjs");
                extjs_jsonVar = compObj.qry2json(q,0,100);
            
            
                
            
        
        

        
    
     
      
        
        
  
        
        

   
               SELECT id as RECORDID, last as LASTNAME
                  FROM  contactform
                  order by recordid
            
            
     
                //format the query for ExtJS json
                compObj = CreateObject("component", "cf_extjs");
                extjs_jsonVar = compObj.qry2json(q,0,100);
            
            
                
            
        
        

        
    
    
    
    
 
 
 
  
  
 
 
  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 
 
  
  
 
 
  
 
 
 
  
 
 
 
  
 
 
 
  
  
   
   
    
   
    
   
  
  
  
 
 
 
  
  
  
   
   
    
   
    
   
  
  
  
 
 
 
  
  
  
  
   
   
   
   
  
   
   
   
   
  
  
  
  
   
   
  
   
  
  
  
  
  
   
   
   
   
    
     
    
    
     
    
    
    
    
     
     
     
     
      
     
     
    
    
    
    
    
   
   
  
  
   
   
    
     
    
    
    
    
     
     
     
      
     
     
     
      
     
     
     
     
    
    
   
   
  
  
  
  
 
 
 
  
 




 
 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   
  
  
  
   
  
  
  
   
  
  
  
   
  
  
  
   
   
   
    
    
    
    
     
     
      
     
      
      
       
       
       
       
        
       
        
       
      
       
      
     
    
    
    
   
    
   
  
  
  
   
   
   
    
   
    
   
    
   
   
   
   
   
   
   
    
     
    
     
    
   
   
   
   
   
    
    
    
    
    
    
     
    
    
     
     
    
    
     
     
     
     
      
     
     
      
      
      
        
      
        
      
      
      
      
      
       
      
      
      
      
      
      
       
      
      
      
       
        
       
        
        
         
        
       
        
        
        
         
          
          
         
        
       
      
     
     
     
    
    
     
    
    
     
    
    
    
     
    
   
   
   
   
    
   
    
   
  
  
  
   
  
 
 
 
 
 
  
   
   
  
  
   
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

  
  
   
   
  
  
   
  
  
  
   
  
  
  
   
  
  
  
   
   
    
    
     
    
     
    
   
   
   
  
  
  
   
   
   
    
    
     
    
     
    
   
   
   
  
  
  
   
   
   
   
    
    
    
    
   
    
    
    
    
   
   
   
   
    
    
   
    
   
   
   
   
   
    
    
    
    
     
      
     
     
      
     
     
     
     
      
      
      
      
       
      
      
     
     
     
     
     
    
    
   
   
    
    
     
      
     
     
     
     
      
      
      
       
      
      
      
       
      
      
      
      
     
     
    
    
   
   
   
   
  
  
  
   
  
 
 
 
  
  
  
  
  
  
  
  
  
     
  
  
  
  
  
  
  
  
   
    
   
   
   
    
   
   
   
   
  
   
  
   
   
  
  
  
  
   
    
    
     
    
   
    
    
     
      
       
       
        
       
      
     
    
    
    
    
     
      
       
       
        
       
      
     
    
   
  
   
    
    
     
    
   
    
    
    
    
    
     
     
      
     
    
     
     
      
     
    
    
    
    
     
      
      
       
      
     
    
   
  
   
    
    
     
    
   
    
    
     
    
   
    
    
     
    
   
  
   
   
    
   
  
   
    
     
    
    
    
     
    
   
  
   
    
    
     
    
   
    
    
     
    
   
    
    
     
    
   
  
  
  
   
  
   
  
    


And here is the code for the form submit:
 








 



 




 





 
    
    
       
          
                Update contactform
                Set 
                    firstName  = 
                   ,lastName  = '#form.lastname#'
                   ,company  = '#form.company#'
                   ,title   = '#form.title#'
                   ,pic   = '#form.pic#'
                  ,email   = '#form.email#'
                   ,webpage  = '#form.webpage#'
                   ,imaddress  = '#form.imaddress#'
                   ,homephone  = '#form.homephone#'
                   ,busphone  = '#form.busphone#'
                   ,mobphone  = '#form.mobphone#'
                   ,fax   = '#form.fax#'
                   ,baddress  = '#form.baddress#'
                   ,mailingaddress = '#form.mailingaddress#'
                   ,mailto   = '#form.mailto#'
                Where id = #form.id#
                 
                
              
                 
              
                  
                  
              
              
        
   
              
                   
              
               
                   
              
      
            
          
                INSERT INTO contactform
                (
                     firstName 
                   , lastName
                   , company
                   , title
                    ,pic
                   , email
                   , webpage
                   , imaddress
                   , homephone
                   , busphone
                   , mobphone
                   , fax
                   , baddress
                   , mailingaddress
                   , mailto
               
                    )
                    VALUES
                (
                    
                    ,
                    ,
                    ,
                    ,
                    ,
                    ,
                    ,
                    ,
                    ,
                  ,
                    ,
                    ,
                    ,
                      ,
                   
                 
                )
              
                
              
                  
                  
              
              
            
              
              
                   
              
               
                   
              
    
        


  
  





#result#
Here is the code for the file upload:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
     <cfsetting showdebugoutput="no">
   <link rel="stylesheet" type="text/css" href="resources/css/ext-all.css">
    <style type="text/css">
        
    </style>

    <script src="adapter/ext/ext-base.js" type="text/javascript"></script>

    <script src="js/ext-all.js" type="text/javascript"></script>    

    <script type="text/javascript">

    // Uploading a file

   /* Ext.BLANK_IMAGE_URL = 'images/s.gif';
*/
var picBox = {
    columnWidth: '100 px',
    bodyStyle: 'padding:10px',
    items: [
        { xtype: 'box',
            autoEl: { tag: 'div',
            html: '<img id="pic" src="' + Ext.BLANK_IMAGE_URL + '" class="img-contact" width="100" heighth="100"/>'
            
            }
        }
    ]
}

var picFiles = { 
    columnWidth: .65,
    layout: 'form',
    labelAlign:'top',
    items: [
        {
            xtype: 'textfield',
            fieldLabel: 'Current',
            labelSeparator: '',
            name: 'currPic',
            id:'currPic',
            readOnly: true,
            disabled:true,
            anchor:'100%'
        },
        {
            xtype: 'textfield',
            fieldLabel: 'New (JPG or PNG only)',
            labelSeparator: '',
            name: 'newPic',
            id:'newPic',
            style:'width: 300px',
            inputType: 'file',
            allowBlank: false
        }
    ]
}

function validateFileExtension(fileName) {

    var exp = /^.*\.(jpg|JPG|png|PNG)$/;
    return exp.test(fileName);
    
}

Ext.onReady(function() {
  /*Ext.BLANK_IMAGE_URL = 'images/s.gif';*/
    var pictUploadForm = new Ext.FormPanel({
        frame: true,
        title: 'Change Picture',
        bodyStyle: 'padding:5px',
        width: 420,
        layout: 'column',
        /*url: 'contact-picture.aspx',*/
        /*url: 'fileuploadtest.cfm',*/
        /*url: 'fileuploadaction2.cfm',*/
        url: 'fileuploadaction2.cfc?method=uploadPic',
        method: 'POST',
        fileUpload: true,
        enctype: 'multipart/form-data',
        items: [picBox, picFiles],
        buttons: [{
            text: 'Upload Picture',
            handler: function() {

            var theForm = pictUploadForm.getForm();
                
                if (!theForm.isValid()) {
                    Ext.MessageBox.alert('Change Picture', 'Please select a picture');
                    return;
                }
                if (!validateFileExtension(Ext.getDom('newPic').value)) {
                    Ext.MessageBox.alert('Change Picture', 'Only JPG or PNG, please.');
                    return;
                }
                theForm.submit({
                    params: { act: 'setPicture', id: 'contact1' }
                    ,
                    waitMsg: 'Uploading picture' ,
                    success: function(form,action){
                                    Ext.MessageBox.alert('Success', 'Processed file on the server');
                                },
                    failure: function(form,action){
                                    Ext.MessageBox.alert('Failure', 'did not process the upload');
                                }
                                        
                })
                /*pictUploadForm.Reset();*/
            }
        }, {
            text: 'Cancel'
        },
        {
            text: 'Reset'
            ,id: 'reset-button'
            ,type: 'reset'
        }
        ]
    });

pictUploadForm.on({
    /*alert('the load has been completed');*/
    
    
    actioncomplete: function(form, action) {
        var result = Ext.decode(action.response.responseText);
        if (action.type == 'load') {
            Ext.MessageBox.alert('pictUploadForm.actioncomplete', 'load has been completed');
            var pic = action.result.data;
            Ext.getDom('pic').src = pic.file;
            Ext.getCmp('currPic').setValue(pic.file);
             if (action.failureType == "connect") { 
                    Ext.MessageBox.alert('pictUploadForm.actionfailed error', 'Form load failed. Could not connect to server.');
                } else {
                    if (action.response.responseText != '') {
                        var result = Ext.decode(action.response.responseText);
                        if(result && result.msg) {            
                            Ext.MessageBox.alert('pictUploadForm.actionfailed error', 'Form load failed with error: ' + action.result.msg);
                        } else {
                            Ext.MessageBox.alert('pictUploadForm.actionfailed error', 'Form load failed with unknown error (possibly missing the "success" field in the json). Action type='+action.type+', failure type='+action.failureType);
                          }
                    } else {
                        Ext.MessageBox.alert('pictUploadForm.actionfailed error', 'Form load returned an empty string instead of json');
                      }
                  }
        }
        if (action.type == 'submit') {
              Ext.MessageBox.alert('pictUploadForm.actioncomplete', 'submit has been completed');
            var pic = action.result.data;
            Ext.getDom('pic').src = pic.file;
            Ext.getDom('currPic').src = pic.file;
          /*  Ext.getCmp('currPic').setValue(pic.file);*/
            Ext.getDom('newPic').value = '';
             if (action.failureType == "connect") { 
                    Ext.MessageBox.alert('pictUploadForm.actionfailed error', 'Form submit failed. Could not connect to server.');
                } else 
                    if (action.failureType == "server") { 
                        // These arent "errors" as such, they are validation issues trapped by the server script and passed back for the user to correct
                    } else {    
                        var result = Ext.decode(action.response.responseText); 
                        if(result && result.msg) {            
                            Ext.MessageBox.alert('pictUploadForm.actionfailed error', 'Form submit failed with error: ' + action.result.msg);
                        } else {
                            Ext.MessageBox.alert('actionfailed Error', 'Form submit returned unknown error. Action type='+action.type+', failure type='+action.failureType);
                            Ext.MessageBox.alert('pictUploadForm.actioncomplete', + action.response.responseText);
                          }
                     
            }   
        }
    }
});
    /*simple.render(document.body);*/
   /* Ext.get('reset-button').removeAllListeners();*/
    pictUploadForm.render(document.body);

    Ext.getDom('newPic').size = 10;

    pictUploadForm.getForm().load({ params: { act: 'getPicture', id: 'contact1' }
    /*,
     waitMsg: 'Loading'*/
      });

});
    
    </script>

</head>
<body style="padding: 20px">
</body>
</html>


Last but not least, here if the CFC for the file upload action.






                


  
  var stcReturn = structnew();
  stcReturn['success'] = true; 
  
  return stcReturn;
  
   
  
    
  

  
 

 
  var stcReturn = structnew();
  stcReturn['success'] = true; 
  stcMsg['msg'] ='successfully uploaded file';
     stcReturn['data'] = structnew();
  stcReturn['data']['Contact Id'] = 'contact1';
  stcReturn['data']['file2'] = "img/" & cffile.clientfile;
  stcReturn['data']['file'] = cffile.clientfile;
    
 
 
 

return stcReturn;
  

   




 


Well it was quite exhausting just uploading these files and making sure the syntax highlighter was working. I will come back later to explain it all. I hope you find it useful.

You might ask me what was hard about this project? Here is my list:
  • I had difficulty connecting the search form to the contact form because the contact form did not have an ID
  • The radio buttons were quite difficult for me to populate and I spent a great deal of time getting them to work
  • It took me days to get the image to display properly on the form
  • I struggled and struggled with getting the window to show
  • I struggled even further populated the fields in the window form
  • The file upload took forever to get working and I thought it would be the impossible dream, you have to get the upload form to work and the updload action cfc
  • Populating the main picture was challenging
  • Closing the window was impossible, window hide did not work but the close button did, I gave up and just let the close button drive the window close
Long story short, this took me several months to put together. I believe the rest of the community would have found it less daunting. I hope my example makes it even easier for you.

ColdFusion and the ExtJS Editor Grid - One row at a time.

Here is the code for a row editor grid. It provides a paging bottom toolbar, a row numberer and the check column. This editor can be tricky. It is supposed to return one row at a time. Sometimes it returns multiple rows. I modified the CFC to handle that. The cfc checks to see if it is passed an array. If it is, we are dealing with multiple rows. If not, we are dealing with one row.

We will start out with the cfm file.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Ext JS Grid with ColdFusion</title>
        <cfsetting showdebugoutput="false">    
        <div style="text-align:center">
      

  <!-- Arquivos utilizados pelo jQuery lightBox plugin -->
        <link rel="stylesheet" href="css/lightbox.css" type="text/css" media="screen" />
        <script src="js/prototype.js" type="text/javascript"></script>
        <script src="js/scriptaculous.js?load=effects,builder" type="text/javascript"></script>
        <script src="js/lightbox.js" type="text/javascript"></script>
 

 
        <style type="text/css">
            body{ color: #333; font: 13px 'Lucida Grande', Verdana, sans-serif;    }
        </style>
        
        <script type="text/javascript">
        function cover_image(val){
        return '<img src=images/'+val+'>';
        }
        </script>
        
        <script type="text/javascript">
        function renderIMG(val) {
        return ('<a href="images/{val}" rel="lightbox" title="my caption" alt="{val}"> <img src="images/{val}" width="100" height="80" border="0" alt=""> </a>');}
        </script>
        
        <script>
                Ext.ux.Lightbox.register('a[rel^=lightbox]');
                Ext.ux.Lightbox.register('a.lb-flower', true); // true to show them as a set
        </script>

        <link rel="stylesheet" type="text/css" href="resources/css/ext-all.css">
          <link rel="stylesheet" type="text/css" href="ux/css/RowEditor.css" />
        <script src="extlightbox.js"></script>
        <script src="adapter/ext/ext-base.js"></script>
        <script src="js/ext-all.js"></script>        
        
        <script src="editgridItemTableMovie4.js"></script>
        <script type="text/javascript" src="ux/RowEditor.js"></script>
        <script type="text/javascript" src="examples/ux/CheckColumn.js"></script>

        <style type="text/css">
        body .x-panel {
            margin-bottom:20px;
        }
        .icon-grid {
            background-image:url(../shared/icons/fam/grid.png) !important;
        }
        #button-grid .x-panel-body {
            border:1px solid #99bbe8;
            border-top:0 none;
        }
        .add {
            background-image:url(../shared/icons/fam/add.gif) !important;
        }
        .option {
            background-image:url(../shared/icons/fam/plugin.gif) !important;
        }
        .remove {
            background-image:url(../shared/icons/fam/delete.gif) !important;
        }
        .save {
            background-image:url(../shared/icons/save.gif) !important;
        }
    </style>

        <style type="text/css">
        /*.x-btn-td {padding-left:200;}*/
        /*.x-btn button {margin-left:100;}*/
        .x-btn-mc {text-align:center;}
        .x-btn-mc {div style=text-align:center;}
        .x-btn-mc {margin-left:100; }
        .ext-gen28 {margin-left:100px; position:relative;}
        </style>
        
        
    
    


          <style type="text/css">
          .x-grid3-col{text-align:right;}
          .x-grid3-col-0{text-align:right; white-space:normal !important;}
          .x-grid3-col-1{text-align:left; white-space:normal !important;}
          .x-grid3-col-2{text-align:left; white-space:normal !important;}
          .x-grid3-col-3{text-align:left; white-space:normal !important;}
          .x-grid3-col-4{text-align:right; white-space:normal !important;}
          .x-grid3-header-offset {width: auto;}
          .x-grid3-row td {white-space:normal !important;}
          .x-grid3-hd {white-space:normal !important;}
          .x-grid3-header-offset {width: auto;}
          .x-grid3-hd-0-0 {white-space:normal !important;}
          .x-grid3-hd-0-1 {white-space:normal !important;}
          .x-grid3-hd-0-2 {white-space:normal !important;}
          .x-grid3-hd-0-3 {white-space:normal !important;}
          .x-grid3-td-1{white-space:normal !important;}
          .x-grid3-td-2 {white-space:normal !important;}
          .x-grid3-td-3 {white-space:normal !important;}
          .x-toolbar-td {align: right !important;}
          .x-toolbar-left {align:center;}
          .x-toolbar-cell {align: center;}
          .x-btn {align: center;}
          .x-btn-noicon {align: center;}
          .x-btn-icon-small-left {align: center;}
          .ext-gen26 {align: center;}
          .x-panel-bl {align: center;}
          .x-btn-t1 {align: center;}
          .x-btn-ml {align: center;}
          .x-btn-mc {align: center;}
          .x-toolbar-left {text-align:center;}
          .x-btn-small {align: center;}
          <!--.x-btn-small style="align:center;"}-->
          .x-btn-left {align: center;}
          <!---.x-grid3-td-4 {white-space:normal !important;}
          .x-grid3-td-5 {white-space:normal !important;}--->
            </style>



    </head>

<!--<script type="application/x-javascript">
window.onload=function(){initlightbox');}
</script>-->
    <body>
        <div class="thumbnail">
        <div id="editgrid"></div>
        </div>
        
    </body>
</html>
Here is the javascript code for the editor:
Ext.onReady(function(){
 
 
 var users = Ext.data.Record.create([
 {name:'ID',allowBlank:false},
 {name:'COVERTHUMB',type:'string'},
 {name:'TITLE',type:'string'},
 {name:'DIRECTOR',type:'string'},
 {name:'RELEASED',type:'date'},
 {name:'RUNTIME',type:'string'},
 {name:'GENRE',type:'string'},
 {name:'TAGLINE',type:'string'},
 {name:'PRICE',type:'float'},
 {name:'AVAILABLE',type:'boolean'}
 ])

 function cover_image(val) {
  return '';
 
}
 
 //The Proxy object will specify URLs to call for different CRUD operations
 var proxy = new Ext.data.HttpProxy({
  method:'POST',    
  api:{
   read: 'movies.cfc?method=getMovies',//Our URL for reading the grid data
   create: 'movies.cfc?method=addMovies',//For Adding a new User (future implementation)
   update: 'movies.cfc?method=editMovieRow'//When a User is updated
  },
  //the function writeSuccess will be called when the Store finishes a succesful write (add/edit) to the server
  listeners: {
   write: {
    fn: writeSuccess
   }
  }
 })
 
 
 //Our JSON Writer, configuration used to updating/adding of records
 var writer = new Ext.data.JsonWriter({
  returnJson: true,
  //We will only send the changed fields
  writeAllFields: true  
 })
 
    // create the Data Store
    var store = new Ext.data.JsonStore({
     totalProperty:'DATASET',//This is how many total records are there in the set.
     root:'ROWS',//The Root of the data.        
  proxy:proxy,
  id:'ID',
  successProperty:'SUCCESS',//What return string data will be set to true/false to let Ext know that server call was succesful or not
  autoSave:false,//we will use event listeners to call save manually
        remoteSort:true,//We will sort server side
        //Base Params are parameters passed in during every call
        baseParams:{             
             returnFormat: 'JSON',
             start: '0',
             limit: '50'
            },
        //We define the JSON Reader for the data. We also need to set the totalProperty, root and idProperty for the dataset here.
        reader: new Ext.data.JsonReader({
          totalProperty:'DATASET',
          root:'ROWS',
          idProperty:'ID'            
         },users
        ),
        //Fields read in
        fields: [
               'ID', 'COVERTHUMB', 'TITLE', 'DIRECTOR','RELEASED', 'RUNTIME', 'GENRE', 'TAGLINE', 'PRICE', 'AVAILABLE'
        ],
  writer:writer,
  //Any Store execeptions will be caught here
  listeners: {   
   exception : function(proxy,type,action,options,res,rs) {
    alert(type);
   }
  }      
    });
 
  
 // Our Form Variable to be used for editor grid
 var varForm = Ext.form;
 
 // the check column is created using a custom plugin
    var checkColumn = new Ext.grid.CheckColumn({
        header: 'Available?',
        dataIndex: 'AVAILABLE',
        width: 55,
  editor: new varForm.Checkbox({
   })
    });
    //We setup the Grid
 var cm = new Ext.grid.ColumnModel({
       columns:[
     new Ext.grid.RowNumberer(),//This will do numbering on the grid for us        
        {
    header: "ID",
   dataIndex:'ID',
   editable: false,
   width:40 
  },
  {
            header: "Cover Thumb",
            dataIndex: 'COVERTHUMB',
            width: 100,
            hidden: false,
            sortable: true,
   /*renderer: imageCellRenderer,*/
   renderer: cover_image,
   /*renderer: renderIMG,*/
   editor: new varForm.TextField({
                allowBlank: false
     }) 
        },
  {   
            header: "Title",
            dataIndex: 'TITLE',
            width: 150,
            hidden:false,            
            sortable: true,   
            editor: new varForm.TextField({
                allowBlank: false
            })
        }
  ,{
            header: "Director",
            dataIndex: 'DIRECTOR',
            width: 100,
            hidden: false,
            sortable: true,
   editor: new varForm.TextField({
                allowBlank: false
            })
        },
  {
            header: "Released",
            dataIndex: 'RELEASED',
   width: 80,
   type: 'date',
            hidden: false,
            sortable: 'true',
   dateFormat: 'M d Y',
   /*release_edit = new Ext.form.DateField({
format: 'm/d/Y'
}),*/
   
   renderer: Ext.util.Format.dateRenderer('M d Y'),
   editor: new varForm.DateField({
                format: 'M d Y'
            })
  },
  {
            header: "Run Time",
            dataIndex: 'RUNTIME',
            width: 50,
            hidden: false,
            sortable: true,
    editor: new varForm.TextField({
                allowBlank: false
    
            })
        },
  

  
  
  /*SELECT id, coverthumb, title, director, runtime, released, genre, tagline, price, available*/
   {
            header: "Genre",
            dataIndex: 'GENRE',
   width: 50,
   hidden: false,
   editor: new varForm.TextField
   ({
                allowBlank: false
  })
        },
  {
            header: "Tagline",
            dataIndex:'TAGLINE',
            width: 250,
            hidden: false,
            sortable: true,
   editor: new varForm.TextField({
                allowBlank: false
            })
        },
  {
            header: "Price",
            dataIndex: 'PRICE',
            width: 50,
            hidden: false,
            sortable: true,
   renderer: 'usMoney',
   editor: new varForm.NumberField({
                allowBlank: true,
    allowNegative: false,
                    maxValue: 100000
   })
        }
  ,

    checkColumn 
    // the plugin instance
           // header: "Available",
//            dataIndex: 'AVAILABLE',
//            width: 100,
//  /* xtype: 'checkbox',*/
//            hidden: false,
//            sortable: true,
//   editor: new varForm.Checkbox({
//   })
       
  ]  
    })
 sm: new Ext.grid.RowSelectionModel({
                singleSelect: true,
                listeners: {
                     rowselect: function(smObj, rowIndex, record) {
                         selRecordStore = record;
                    }
               }
            })
 //  var rowEditor = new Ext.ux.grid.RowEditor({
//            saveText: 'Update',
//            listeners: {
//                afteredit: syncStore
//            }
//        });
  // create the editor grid
    var grid = new Ext.grid.EditorGridPanel({
        width:1100,
        height:500,                
        title:'Browse through the Items in Donnas Shop',
        store: store,
        trackMouseOver:true,
        disableSelection:false,
        loadMask: true,
  stripeRows: true,
  collapsible: true,
  columnLines: true,
  iconcls:'icon-grid',
  cm:cm,//Our column model
  frame:true,//Make it more nicer looking (?)
        clicksToEdit: 1,//One click on row data will bring on edit box
  renderTo:'editgrid',
  region: 'center',
   // inline buttons
    buttons:[{text:'Save'},{xtype:'tbfill'},{text:'Cancel'},{xtype:'tbfill'}],
        buttonAlign:'center'
  /*
  ,
  plugins: [new Ext.ux.grid.RowEditor()
  ]
  */
  ,

      
      
   
  
    // paging bar on the bottom
       bbar: new Ext.PagingToolbar({
            pageSize: 50,
            store: store,
            displayInfo: true,
            displayMsg: 'Displaying Records {0} - {1} of {2}',
            emptyMsg: "No Records to display", 
   items:[
     '-', {
     pressed: true,
     enableToggle:true,
     text: 'Show Preview',
     iconCls: 'icons/user_suit.gif'
     //cls: 'x-tbar-loading',
     },'-', {
     pressed: true,
     enableToggle:true,
     text: 'Show Preview',
     icon: 'icons/user_female.gif'
     //cls: 'x-tbar-loading'
     }]
  }) ,
    sm: new Ext.grid.RowSelectionModel({
                singleSelect: true,
                listeners: {
                     rowselect: function(smObj, rowIndex, record) {
                         selRecordStore = record;
                    }
               }
            }),
     // inline toolbars
  tbar: new Ext.Toolbar({
   items : [
    {
     xtype:'tbtext',
     text:'Welcome. Click here to edit .',
     itemId:'tbartext',
     id:'tbartext'
    },
    {
            text:'Add A New Store Item',
            tooltip:'Add a new row',
            icon:'icons/fam/add.gif',
   handler: function() {
     Ext.Ajax.request({
     url: 'movies.cfc?method=addMovie',
     params: {
     action: 'create',
     title: 'New Movie'
     },
     success: function(resp,opt) {
     var INSERT_ID = Ext.util.JSON.decode(
     resp.responseText
     ).INSERT_ID;
     grid.getStore().insert(0,
     new users({
     /*var user1 = new users({*/
     ID: INSERT_ID,
      
     COVERTHUMB: 'blankmovie.jpg',
     TITLE: 'Great Movie',
     DIRECTOR: 'Movie Director',
     RUNTIME: 2,
     RELEASED: '03/15/2011',
     GENRE: 1,
     TAGLINE: 'Great Movie',
     PRICE: 22.99,
     AVAILABLE: 1
     }, INSERT_ID)
     );
     grid.startEditing(0,0);
     },
     failure: function(resp,opt) {
     Ext.Msg.alert('Error','Unable to add movie');
     }
     });
     
   
   
   
   
   
   
                store.insert(0, Users);
                grid.getView().refresh();
                grid.getSelectionModel().selectRow(0);
           
   }
 
        }, '-', {
            text:'Options',
            tooltip:'Blah blah blah blaht',
            icon:'icons/fam/connect.gif'
        },'-',{
   xtype:'tbbutton',
            text:'Remove Movie',
            tooltip:'Remove the selected movie',
            icon:'icons/fam/delete.gif',
   handler: function() {
var sm = grid.getSelectionModel(),
sel = sm.getSelected();
if (sm.hasSelection()){
Ext.Msg.show({
/*Download from Wow! eBook 
Editor Grids
[ 136 ]*/
title: 'Remove Movie',
buttons: Ext.MessageBox.YESNOCANCEL,
msg: 'Remove '+sel.data.TITLE+'?',
fn: function(btn){
if (btn == 'yes'){
Ext.Ajax.request({
url: 'movies.cfc?method=removeMovies',
params: {
method: 'removeMovies',
action: 'destroy',
/*id: e.record.ID*/
/*id: e.data.ID*/
id: sel.data.ID
},
success: function(resp,opt) {
grid.getStore().remove(sel);
},
failure: function(resp,opt) {
Ext.Msg.alert('Error',
'Unable to delete movie');
}
});
}
}
});
};
}
/*}*/
     
           /* // Place a reference in the GridPanel
            ref: '../removeButton',
            disabled: true*/
        }
   ]
   
   
  }
  
  ),
  
  
   listeners: {
    afteredit: { //This listener event function will be called when user finishes editing data box and tabs/enters/clicks out.
     fn: editStore
    }
  }  
  
    });
 
 
 function editStore(e)
 {
  //Call our store's save event that will fire things off'
  this.store.save();  
 }
 function onAdd(e)
 {
  var u = new this.store.recordType({
            ItemID: '',
            ItemIdentifier: 'VP1',
            ItemDescription: 'My Description',
   ItemTitle: 'President'
        });
        this.stopEditing();
        this.store.insert(0, u);
        this.startEditing(0, 1);
    }
 
 function syncStore(rowEditor, changes, r, rowIndex) {
store.save();
}
 
 /*write(dataproxy,action,data,response,record,options)*/
 function writeSuccess(store, action, data, response, rs, options )
/* function writeSuccess(store, action, result, res, rs, resp, opt)*/
 {
  //Get Toolbar Object
  var tbar = grid.getTopToolbar();
  //Remove current text
  tbar.remove("tbartext");
  
  tbar.addText({
   xtype:'tbtext',
   
   text:'Success !! ' + action,
   itemId:'tbartext'
   }
  );
  //Redo layout to render new toolbar text
  tbar.doLayout();
 }
 
 //Default Sort set for the grid load call
 store.setDefaultSort('ID','ASC');
   
    // trigger the data store load
    store.load(); 
 
 
 
});

And finally here is the CFC. Take a look at the method to editMovieRow as it is designed to handle one row or an array. If you included all the javascript files mentioned in the html, this should all work for you. Oh, make sure you are running Ext JS 3.2. If you do not have those files you can get them from here: https://code.google.com/p/extjs-public/source/browse/tags/extjs-3.2.1?r=117#extjs-3.2.1





    
    
    
    
  
  
  
 
    
        
    
   
  
  
  
  
  
  
  
  
  
    
  
  
        
        SELECT ID as id, coverthumb, title, director, runtime, released, genre, tagline, price, available
                    
  FROM movies
  
        
        
  
  
  
  
 
 
    
    
   
  
  
  
  
  
  
  
  
  
    
  
  
        
   
                SELECT ID, COVERTHUMB, TITLE, DIRECTOR, URL, RUNTIME, RELEASED, PRICE, AVAILABLE
                 FILMED_IN,
                  GENRE, TAGLINE, PRICE, AVAILABLE, DESCRIPTION
  FROM movies
        Order by GENRE
  
        
     
        
        
  
   

  
 
                
  
  
 

    
    
  
  
  
  
   
    
  
  
  
  
  
    
   
   
            
            
   
            
            
   
            
            
            
          
   
      
  
  
  
  
  
 

    
    


 
 
 
   
            // Define the local scope.
            var LOCAL = StructNew(); 
            
            // Get the column names as an array.
            LOCAL.Columns = ListToArray( ARGUMENTS.Data.ColumnList ); 
            
            // Create a structure that will hold the Store.
            LOCAL.Store.columns = LOCAL.Columns;
            
            // Create a second array for the data
            LOCAL.DataArray = ArrayNew(1); 
            
            // Loop over the query.
            for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
            
                // Create an array for this row
                LOCAL.Row = ArrayNew(1);  
                
                // Loop over the columns in this row.
                for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen( LOCAL.Columns ) ; LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
        
                    // Get a reference to the query column.
                    LOCAL.ColumnName = LOCAL.Columns[ LOCAL.ColumnIndex ];  
                    
                    // Store the query cell value into the array by key.
                    LOCAL.Row[ LOCAL.ColumnIndex ] = ARGUMENTS.Data[ LOCAL.ColumnName ][ LOCAL.RowIndex ];
            
                }
                
                // Add the row array to the data array.
                ArrayAppend( LOCAL.DataArray, LOCAL.Row );
            
            }
            
            // Add the Data array to the query array
            LOCAL.Store.data = LOCAL.DataArray ;
                
            // Return the store
            return( LOCAL.Store );
            
            
            
    


 
      
      
      
      
      
           arrRecords = arrayNew(1);
           if(arguments.start==0) {
                counter = 1;
           }
           else {
                counter = arguments.start;
           }
           for(i=1;i<=arguments.limit;i++)  {
                strResults = structNew();
                for(x=1;x<=listLen(clist);x++) {
                     strResults[ucase(listGetAt(clist,x))] = query[listGetAt(clist,x)][counter];
                }
                arrRecords[i] = strResults;
                counter = counter+1;
          }
          return arrRecords;
     
    
    
    
    
     
         
  
       
  
   
  
  
  
        
        
        
   
        
        
  
   
  
   
  
   
        
   
        
   
  
          
  
   
        
   
        
   
  
  
       
              
                 UPDATE movies
                  SET #PreserveSingleQuotes(strUpdQuery)#
                  WHERE id = #Arguments.id# 
              
        
              
                  UPDATE movies
                  SET Price = #Arguments.value#
                  WHERE id = #Arguments.id#
              
        
  
        
  
        
  
  
  
  
  
 
    
    
  
  
        
  
  
       
            
            
            
            
          
           
                
                
            
    
            
                UPDATE movies
                SET
                
                    TITLE = '#stcData[x].TITLE#',
                
                
                    DIRECTOR = '#stcData[x].DIRECTOR#',
                
                
                    TAGLINE = '#stcData[x].TAGLINE#',
                
                
                    
                            GENRE = 1,
                     
                            GENRE = '#stcData[x].GENRE#',
                     
                
                
                    
                        RELEASED = '11/15/2010',
                     
                        RELEASED = '#DateFormat(left(stcData[x].RELEASED,10),"mm/dd/yyyy")#',
                    
                
                
                     
                      RUNTIME = 2
                     
                        RUNTIME = #stcData[x].RUNTIME#
                    
                
               
                WHERE ID = #stcData[x].ID#
            
           
           
          
    

  
            
        
        
        
   
        
        
  
  
    
    
        

      
   UPDATE movies
            SET
            
            
    COVERTHUMB = '#stcData.COVERTHUMB#',
         
            
    TITLE = '#stcData.TITLE#',
         
            
    DIRECTOR = '#stcData.DIRECTOR#',
         
                    
   
                  
                      GENRE = #stcData.GENRE#,
                  
                      GENRE = 1,
                 
            
                      GENRE = 1,
            
             
   
    TAGLINE = '#stcData.TAGLINE#',
         
         
            
               
                   
                 RELEASED = '10/27/2012'
                
                 RELEASED = '#stcData.RELEASED#'
                
         
            
           
   WHERE ID = #stcData.id#
  
      
        
        
            
        
  
       
  
  
  
  
 

    
    
  
  
  
  
  
  
  
  
  
  
   Delete from movies
   WHERE id = #Arguments.id#
  
  
  
  
        
        
  
  
  
  
 
    
    
    
    
     
  
         
  
  
  
        
  
  
      
        
  
  
  
        
        
          Insert into Movies
        (   TITLE, DIRECTOR, RELEASED, GENRE, TAGLINE, COVERTHUMB, 
        AVAILABLE,
     RUNTIME,
         PRICE 
        )
        Values
            ( 'New TITLE', 'DIRECTOR', 03/01/2011, '1', 'Great Movie', 'blankmovie.jpg', 
       1,
         1,
         24.99
  )
      
        
        
        
         
         Select max(ID) as myID from movies
     where TITLE = 'New TITLE' 
               
                    
           
           
            
           
           
        
            
          
           
           
         
  
  
 

Enjoy!