Friday, March 22, 2013

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!

No comments:

Post a Comment