Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / grid / Panel.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @author Aaron Conran
17  * @class Ext.grid.Panel
18  * @extends Ext.panel.Table
19  *
20  * Grids are an excellent way of showing large amounts of tabular data on the client side. Essentially a supercharged 
21  * `<table>`, GridPanel makes it easy to fetch, sort and filter large amounts of data.
22  * 
23  * Grids are composed of 2 main pieces - a {@link Ext.data.Store Store} full of data and a set of columns to render.
24  *
25  * {@img Ext.grid.Panel/Ext.grid.Panel1.png Ext.grid.Panel component}
26  *
27  * ## Basic GridPanel
28  *
29  *     Ext.create('Ext.data.Store', {
30  *         storeId:'simpsonsStore',
31  *         fields:['name', 'email', 'phone'],
32  *         data:{'items':[
33  *             {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"},
34  *             {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"},
35  *             {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"},                        
36  *             {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"}            
37  *         ]},
38  *         proxy: {
39  *             type: 'memory',
40  *             reader: {
41  *                 type: 'json',
42  *                 root: 'items'
43  *             }
44  *         }
45  *     });
46  *     
47  *     Ext.create('Ext.grid.Panel', {
48  *         title: 'Simpsons',
49  *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
50  *         columns: [
51  *             {header: 'Name',  dataIndex: 'name'},
52  *             {header: 'Email', dataIndex: 'email', flex:1},
53  *             {header: 'Phone', dataIndex: 'phone'}
54  *         ],
55  *         height: 200,
56  *         width: 400,
57  *         renderTo: Ext.getBody()
58  *     });
59  * 
60  * The code above produces a simple grid with three columns. We specified a Store which will load JSON data inline. 
61  * In most apps we would be placing the grid inside another container and wouldn't need to use the
62  * {@link #height}, {@link #width} and {@link #renderTo} configurations but they are included here to make it easy to get
63  * up and running.
64  * 
65  * The grid we created above will contain a header bar with a title ('Simpsons'), a row of column headers directly underneath
66  * and finally the grid rows under the headers.
67  * 
68  * ## Configuring columns
69  * 
70  * By default, each column is sortable and will toggle between ASC and DESC sorting when you click on its header. Each
71  * column header is also reorderable by default, and each gains a drop-down menu with options to hide and show columns.
72  * It's easy to configure each column - here we use the same example as above and just modify the columns config:
73  * 
74  *     columns: [
75  *         {
76  *             header: 'Name',
77  *             dataIndex: 'name',
78  *             sortable: false,
79  *             hideable: false,
80  *             flex: 1
81  *         },
82  *         {
83  *             header: 'Email',
84  *             dataIndex: 'email',
85  *             hidden: true
86  *         },
87  *         {
88  *             header: 'Phone',
89  *             dataIndex: 'phone',
90  *             width: 100
91  *         }
92  *     ]
93  * 
94  * We turned off sorting and hiding on the 'Name' column so clicking its header now has no effect. We also made the Email
95  * column hidden by default (it can be shown again by using the menu on any other column). We also set the Phone column to
96  * a fixed with of 100px and flexed the Name column, which means it takes up all remaining width after the other columns 
97  * have been accounted for. See the {@link Ext.grid.column.Column column docs} for more details.
98  * 
99  * ## Renderers
100  * 
101  * As well as customizing columns, it's easy to alter the rendering of individual cells using renderers. A renderer is 
102  * tied to a particular column and is passed the value that would be rendered into each cell in that column. For example,
103  * we could define a renderer function for the email column to turn each email address into a mailto link:
104  * 
105  *     columns: [
106  *         {
107  *             header: 'Email',
108  *             dataIndex: 'email',
109  *             renderer: function(value) {
110  *                 return Ext.String.format('<a href="mailto:{0}">{1}</a>', value, value);
111  *             }
112  *         }
113  *     ]
114  * 
115  * See the {@link Ext.grid.column.Column column docs} for more information on renderers.
116  * 
117  * ## Selection Models
118  * 
119  * Sometimes all you want is to render data onto the screen for viewing, but usually it's necessary to interact with or 
120  * update that data. Grids use a concept called a Selection Model, which is simply a mechanism for selecting some part of
121  * the data in the grid. The two main types of Selection Model are RowSelectionModel, where entire rows are selected, and
122  * CellSelectionModel, where individual cells are selected.
123  * 
124  * Grids use a Row Selection Model by default, but this is easy to customise like so:
125  * 
126  *     Ext.create('Ext.grid.Panel', {
127  *         selType: 'cellmodel',
128  *         store: ...
129  *     });
130  * 
131  * Specifying the `cellmodel` changes a couple of things. Firstly, clicking on a cell now
132  * selects just that cell (using a {@link Ext.selection.RowModel rowmodel} will select the entire row), and secondly the
133  * keyboard navigation will walk from cell to cell instead of row to row. Cell-based selection models are usually used in
134  * conjunction with editing.
135  * 
136  * {@img Ext.grid.Panel/Ext.grid.Panel2.png Ext.grid.Panel cell editing}
137  *
138  * ## Editing
139  * 
140  * Grid has built-in support for in-line editing. There are two chief editing modes - cell editing and row editing. Cell
141  * editing is easy to add to your existing column setup - here we'll just modify the example above to include an editor
142  * on both the name and the email columns:
143  * 
144  *     Ext.create('Ext.grid.Panel', {
145  *         title: 'Simpsons',
146  *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
147  *         columns: [
148  *             {header: 'Name',  dataIndex: 'name', field: 'textfield'},
149  *             {header: 'Email', dataIndex: 'email', flex:1, 
150  *                 field:{
151  *                     xtype:'textfield',
152  *                     allowBlank:false
153  *                 }
154  *             },
155  *             {header: 'Phone', dataIndex: 'phone'}
156  *         ],
157  *         selType: 'cellmodel',
158  *         plugins: [
159  *             Ext.create('Ext.grid.plugin.CellEditing', {
160  *                 clicksToEdit: 1
161  *             })
162  *         ],
163  *         height: 200,
164  *         width: 400,
165  *         renderTo: Ext.getBody()
166  *     });
167  * 
168  * This requires a little explanation. We're passing in {@link #store store} and {@link #columns columns} as normal, but 
169  * this time we've also specified a {@link #field field} on two of our columns. For the Name column we just want a default
170  * textfield to edit the value, so we specify 'textfield'. For the Email column we customized the editor slightly by 
171  * passing allowBlank: false, which will provide inline validation.
172  * 
173  * To support cell editing, we also specified that the grid should use the 'cellmodel' {@link #selType}, and created an
174  * instance of the {@link Ext.grid.plugin.CellEditing CellEditing plugin}, which we configured to activate each editor after a
175  * single click.
176  * 
177  * {@img Ext.grid.Panel/Ext.grid.Panel3.png Ext.grid.Panel row editing}
178  *
179  * ## Row Editing
180  * 
181  * The other type of editing is row-based editing, using the RowEditor component. This enables you to edit an entire row
182  * at a time, rather than editing cell by cell. Row Editing works in exactly the same way as cell editing, all we need to
183  * do is change the plugin type to {@link Ext.grid.plugin.RowEditing}, and set the selType to 'rowmodel':
184  * 
185  *     Ext.create('Ext.grid.Panel', {
186  *         title: 'Simpsons',
187  *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
188  *         columns: [
189  *             {header: 'Name',  dataIndex: 'name', field: 'textfield'},
190  *             {header: 'Email', dataIndex: 'email', flex:1, 
191  *                 field:{
192  *                     xtype:'textfield',
193  *                     allowBlank:false
194  *                 }
195  *             },
196  *             {header: 'Phone', dataIndex: 'phone'}
197  *         ],
198  *         selType: 'rowmodel',
199  *         plugins: [
200  *             Ext.create('Ext.grid.plugin.RowEditing', {
201  *                 clicksToEdit: 1
202  *             })
203  *         ],
204  *         height: 200,
205  *         width: 400,
206  *         renderTo: Ext.getBody()
207  *     });
208  * 
209  * Again we passed some configuration to our {@link Ext.grid.plugin.RowEditing} plugin, and now when we click each row a row
210  * editor will appear and enable us to edit each of the columns we have specified an editor for.
211  * 
212  * ## Sorting & Filtering
213  * 
214  * Every grid is attached to a {@link Ext.data.Store Store}, which provides multi-sort and filtering capabilities. It's
215  * easy to set up a grid to be sorted from the start:
216  * 
217  *     var myGrid = Ext.create('Ext.grid.Panel', {
218  *         store: {
219  *             fields: ['name', 'email', 'phone'],
220  *             sorters: ['name', 'phone']
221  *         },
222  *         columns: [
223  *             {text: 'Name',  dataIndex: 'name'},
224  *             {text: 'Email', dataIndex: 'email'}
225  *         ]
226  *     });
227  * 
228  * Sorting at run time is easily accomplished by simply clicking each column header. If you need to perform sorting on 
229  * more than one field at run time it's easy to do so by adding new sorters to the store:
230  * 
231  *     myGrid.store.sort([
232  *         {property: 'name',  direction: 'ASC'},
233  *         {property: 'email', direction: 'DESC'},
234  *     ]);
235  * 
236  * {@img Ext.grid.Panel/Ext.grid.Panel4.png Ext.grid.Panel grouping}
237  * 
238  * ## Grouping
239  * 
240  * Grid supports the grouping of rows by any field. For example if we had a set of employee records, we might want to 
241  * group by the department that each employee works in. Here's how we might set that up:
242  * 
243  *     var store = Ext.create('Ext.data.Store', {
244  *         storeId:'employeeStore',
245  *         fields:['name', 'senority', 'department'],
246  *         groupField: 'department',
247  *         data:{'employees':[
248  *             {"name":"Michael Scott", "senority":7, "department":"Manangement"},
249  *             {"name":"Dwight Schrute", "senority":2, "department":"Sales"},
250  *             {"name":"Jim Halpert", "senority":3, "department":"Sales"},
251  *             {"name":"Kevin Malone", "senority":4, "department":"Accounting"},
252  *             {"name":"Angela Martin", "senority":5, "department":"Accounting"}                        
253  *         ]},
254  *         proxy: {
255  *             type: 'memory',
256  *             reader: {
257  *                 type: 'json',
258  *                 root: 'employees'
259  *             }
260  *         }
261  *     });
262  *     
263  *     Ext.create('Ext.grid.Panel', {
264  *         title: 'Employees',
265  *         store: Ext.data.StoreManager.lookup('employeeStore'),
266  *         columns: [
267  *             {header: 'Name',  dataIndex: 'name'},
268  *             {header: 'Senority', dataIndex: 'senority'}
269  *         ],        
270  *         features: [{ftype:'grouping'}],
271  *         width: 200,
272  *         height: 275,
273  *         renderTo: Ext.getBody()
274  *     });
275  * 
276  * ## Infinite Scrolling
277  *
278  * Grid supports infinite scrolling as an alternative to using a paging toolbar. Your users can scroll through thousands
279  * of records without the performance penalties of renderering all the records on screen at once. The grid should be bound
280  * to a store with a pageSize specified.
281  *
282  *     var grid = Ext.create('Ext.grid.Panel', {
283  *         // Use a PagingGridScroller (this is interchangeable with a PagingToolbar)
284  *         verticalScrollerType: 'paginggridscroller',
285  *         // do not reset the scrollbar when the view refreshs
286  *         invalidateScrollerOnRefresh: false,
287  *         // infinite scrolling does not support selection
288  *         disableSelection: true,
289  *         // ...
290  *     });
291  * 
292  * ## Paging
293  *
294  * Grid supports paging through large sets of data via a PagingToolbar or PagingGridScroller (see the Infinite Scrolling section above).
295  * To leverage paging via a toolbar or scroller, you need to set a pageSize configuration on the Store.
296  *
297  *     var itemsPerPage = 2;   // set the number of items you want per page
298  *     
299  *     var store = Ext.create('Ext.data.Store', {
300  *         id:'simpsonsStore',
301  *         autoLoad: false,
302  *         fields:['name', 'email', 'phone'],
303  *         pageSize: itemsPerPage, // items per page
304  *         proxy: {
305  *             type: 'ajax',
306  *             url: 'pagingstore.js',  // url that will load data with respect to start and limit params
307  *             reader: {
308  *                 type: 'json',
309  *                 root: 'items',
310  *                 totalProperty: 'total'
311  *             }
312  *         }
313  *     });
314  *     
315  *     // specify segment of data you want to load using params
316  *     store.load({
317  *         params:{
318  *             start:0,    
319  *             limit: itemsPerPage
320  *         }
321  *     });
322  *     
323  *     Ext.create('Ext.grid.Panel', {
324  *         title: 'Simpsons',
325  *         store: store,
326  *         columns: [
327  *             {header: 'Name',  dataIndex: 'name'},
328  *             {header: 'Email', dataIndex: 'email', flex:1},
329  *             {header: 'Phone', dataIndex: 'phone'}
330  *         ],
331  *         width: 400,
332  *         height: 125,
333  *         dockedItems: [{
334  *             xtype: 'pagingtoolbar',
335  *             store: store,   // same store GridPanel is using
336  *             dock: 'bottom',
337  *             displayInfo: true
338  *         }],
339  *         renderTo: Ext.getBody()
340  *     }); 
341  * 
342  * {@img Ext.grid.Panel/Ext.grid.Panel5.png Ext.grid.Panel grouping}
343  * 
344  * @docauthor Ed Spencer
345  */
346 Ext.define('Ext.grid.Panel', {
347     extend: 'Ext.panel.Table',
348     requires: ['Ext.grid.View'],
349     alias: ['widget.gridpanel', 'widget.grid'],
350     alternateClassName: ['Ext.list.ListView', 'Ext.ListView', 'Ext.grid.GridPanel'],
351     viewType: 'gridview',
352     
353     lockable: false,
354     
355     // Required for the Lockable Mixin. These are the configurations which will be copied to the
356     // normal and locked sub tablepanels
357     normalCfgCopy: ['invalidateScrollerOnRefresh', 'verticalScroller', 'verticalScrollDock', 'verticalScrollerType', 'scroll'],
358     lockedCfgCopy: ['invalidateScrollerOnRefresh'],
359     
360     /**
361      * @cfg {Boolean} columnLines Adds column line styling
362      */
363     
364     initComponent: function() {
365         var me = this;
366
367         if (me.columnLines) {
368             me.setColumnLines(me.columnLines);
369         }
370         
371         me.callParent();
372     },
373     
374     setColumnLines: function(show) {
375         var me = this,
376             method = (show) ? 'addClsWithUI' : 'removeClsWithUI';
377         
378         me[method]('with-col-lines')
379     }
380 });