Sunday, January 27, 2013

ExtJS 3.2 Cell Editor Grid

I have seen many requests for example ExtJS grids working with ColdFusion. We are all familiar with Cutter Blades examples throughout Sencha and on various ColdFusion blogs. If you do not have a link to his site here it is: http://www.cutterscrossing.com/. Our other savior has been Kumar who has posted a number of articles about grids. Here is his blogging site: http://www.coldfusion-ria.com/Blog/index.cfm/2009/7/13/ExtJS-30-Grid-with-ColdFusion-8

Both of them post excellent examples. My Best Examples came from the ExtJS CookBook by Jorges Ramon and Learning ExtJS and Learning ExtJS 3.2 by Shea Frederick, Colin Ramsay, Steve 'Cutter' Blades and Nigel White.

After you have seen the examples of the Sencha site you know there are many variations of Grids available. My two favorite grids are the cell editor grid and the row editor grid.

This article presents the cell editor grid. The cell editor grid allows you to edit one cell in a grid at a time and it fires an event that calls your cfc and updates that particular column for that particular record.

The following example presents some other nice treats including a paging grid, the row numberer, and the checkbox model.

First I start out with my cfm page. I include a large number of style tages to support word wrapping in the columns and other items too lengthly to mention here. Feel free to disregard them. The main thing you need are the correct extjs files. I also included scriptalicous and lightbox tags because I love grids that use lightbox to expand the image. You do not need these. You just need the extjs files and the javascript file you will be using. In this case I am calling the file editgridcellMovie.js.

Blogger removes the hmtl tags. So create a new cfm page, set up the html opening and closing tags, insert the code below inside the opening and closing header tags. Put the divs at the end inside the body tags and you will be good to go.

In case you are not familiar with this version of syntax highlighter, you just double click the code and it copies it into note pad for you. You will not see the old toolbar that allows you to select for copying.

        
        
        
        
  Ext JS Grid with ColdFusion
               
      

        
 
 
 
 
 

       
 

  

 
        
   
 
 
        
        
 
 

 
              
Now to the Javascript File. I am sure after all of your reading and searching it should make sense. If not, I will follow up with explanations of each piece.
Ext.onReady(function(){
 
 
 var Movie = 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'},
 {name:'TAGLINE',type:'string'},
 {name:'PRICE',type:'float'},
 {name:'AVAILABLE',type:'boolean'}
 ])
//image path
var IMG_LOC = 'images/';

//renderer function
function cover_image(val) {
    /*return '';*/
  return '';
  /*return '';*/
}


 var genres = new Ext.data.SimpleStore({
   fields: ['id', 'GENRE'],
   data : [
   ['0','New Genre'],
   ['1','Comedy'],
   ['2','Drama'],
   ['3','Action'],
   ['4','Horror'],
   ['5','Adult']
   ]
});
 /*retrieves current date*/
 var currentDate = '10/27/1955';
/* new Date().format('Y-m-d')new Date().format('Y-m-d')*/

 /* THIS CODE SECTION SHOWS HOW TO CALL THE GENRES DATABASE TABLE WHEN YOU CHOOSE NOT TO USE THE SIMPLE STORE
 var genres = new Ext.data.Store({
            reader: new Ext.data.JsonReader({
                fields: ['ID', 'genre_name'],
                root: 'ROWS'
            }),

            proxy: new Ext.data.HttpProxy({
                url: 'genres3.cfc?method=getGenres'
            }),
            autoLoad: true
        });
        genres.load();*/
 
  var genre_edit = new Ext.form.ComboBox({
            typeAhead: true,
            forceSelection: true,
            triggerAction: 'all',
            mode: 'local',
            store: genres,
            displayField:'GENRE',
            valueField: 'id',
            listeners: {
     //select: function(field, rec, selIndex){
//     if (selIndex == 0){
//     Ext.Msg.prompt('New Genre', 'Name', Ext.emptyFn);
     select: function(field, rec, idx){
                    if (i === 0){
                        Ext.Msg.prompt('New Genre','Name',Ext.emptyFn);
                    }
                }
            }
        });

 

 function genre_name(val){
            return genres.queryBy(function(rec){
                return rec.data.id == val;
            }).itemAt(0).data.GENRE;
        }

 
 
    // create the Data Store
 
  var store = new Ext.data.Store({
            url: 'movies.cfc?method=getMovies',
            reader: new Ext.data.JsonReader({
                root:'ROWS',
                totalProperty:'DATASET',
                id:'ID'
            }, Movie),
            autoLoad: true,
    fields: [
             'ID', 'COVERTHUMB', 'TITLE', 'DIRECTOR','RELEASED', 'RUNTIME', 'GENRE', 'TAGLINE', 'PRICE', 'AVAILABLE']
        });
  
  
 
 
 // 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,
   editor: new varForm.TextField({
                allowBlank: false
            }) 
   /*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: 150,
            hidden: false,
            sortable: true,
   editor: new varForm.TextField({
                allowBlank: false
            })
        },
  {
            header: "Released",
            dataIndex: 'RELEASED',
   width: 50,
   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: genre_edit,
   renderer: genre_name  
   
   
   /*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;
                    }
               }
            })

  // create the editor grid
    var grid = new Ext.grid.EditorGridPanel({
        width:1200,
        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',

      
      
   
  
    // 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: {
                afteredit: function(e){
                    Ext.Ajax.request({
                        url: 'movies.cfc?method=editMovieCell',
                        params: {
                            action: 'update',
                            id: e.record.id,
       
                            field: e.field,
                            value: e.value
                        },
                        success: function(resp,opt) {
                            e.record.commit();
        var success_message = Ext.util.JSON.decode(
                                response.message
                            ).response.message;
       Ext.alert('success', success_message);
      },
                        failure: function(resp,opt) {
                            e.record.reject();
                        }
                    });
                }
            },
            keys: [{
                key: 46,
                fn: function(key,e){
                    var sm = grid.getSelectionModel(),
                        sel = sm.getSelected();
                    if (sm.hasSelection()){
                        Ext.Msg.show({
                            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: {
                                            action: 'destroy',
                                            id: sel.data.ID
                                        },
                                        success: function(resp,opt) { 
                                            grid.getStore().remove(sel); 
                                        },
                                        failure: function(resp,opt) { 
                                            Ext.Msg.alert('Error','Unable to delete movie'); 
                                        }
                                    });
                                }
                            }
                        });
                    }
                },
                ctrl: false,
                stopEvent: true
            }],
            tbar: [{
                text: 'Add Movie',
               /* icon: 'images/table_add.png',*/
    icon: 'icons/fam/add.gif',
                cls: 'x-btn-text-icon',
                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 Movie({
                                ID: INSERT_ID,
    COVERTHUMB: 'blankmovie.jpg',
                                TITLE: 'New Movie',
                                DIRECTOR: 'Bud Hines',
                                RUNTIME: '2',
                                RELEASED: '10/27/2002',
                                GENRE: '1',
        
                                TAGLINE: 'What a 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');
                        }
                    });
                }
            },{
                text: 'Remove Movie',
               /* icon: 'images/table_delete.png',*/
     icon: 'icons/fam/delete.gif',
                cls: 'x-btn-text-icon',
                handler: function() {
                    var sm = grid.getSelectionModel(),
                        sel = sm.getSelected();
                    if (sm.hasSelection()){
                        Ext.Msg.show({
                            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: {
                                            action: 'destroy',
                                            id: sel.data.ID
                                        },
                                        success: function(resp,opt) { 
                                            grid.getStore().remove(sel); 
                                        },
                                        failure: function(resp,opt) { 
                                            Ext.Msg.alert('Error','Unable to delete movie'); 
                                        }
                                    });
                                }
                            }
                        });
                    }
                }
            }]
        });
    });

And here is the cfc with all the functions to handle the get movies, edit movie cell, remove movie and add movie functionality.





    
    
    
    
  
  
  
 
    
        
    
   
  
  
  
  
  
  
  
  
  
    
  
  
        
        SELECT ID as id, coverthumb, title, director, runtime, released, genre, tagline, price, available
                    
  FROM movies
  
        
        
  
  
  
  
 
 
    
    
  
  
  
  
   
    
  
  
  
  
  
    
   
   
            
            
   
            
            
   
            
            
            
          
   
      
  
  
  
  
  
 

    
    


 
 
 
   
            // 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#
              
        
  
        
  
        
  
  
  
  
  
 
    
    
    
    
  
  
  
  
  
  
  
  
  
  
   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' 
               
                    
           
           
            
           
           
        
            
          
           
           
         
  
  
 

No comments:

Post a Comment