Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / Paging.html
1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-toolbar.Paging-method-constructor'><span id='Ext-toolbar.Paging'>/**
2 </span></span> * @class Ext.toolbar.Paging
3  * @extends Ext.toolbar.Toolbar
4  * &lt;p&gt;As the amount of records increases, the time required for the browser to render
5  * them increases. Paging is used to reduce the amount of data exchanged with the client.
6  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
7  * scrollbars will be added.&lt;/p&gt;
8  * &lt;p&gt;Paging is typically handled on the server side (see exception below). The client sends
9  * parameters to the server side, which the server needs to interpret and then respond with the
10  * appropriate data.&lt;/p&gt;
11  * &lt;p&gt;&lt;b&gt;Ext.toolbar.Paging&lt;/b&gt; is a specialized toolbar that is bound to a {@link Ext.data.Store}
12  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
13  * of data into the &lt;tt&gt;{@link #store}&lt;/tt&gt; by passing {@link Ext.data.Store#paramNames paramNames} used for
14  * paging criteria.&lt;/p&gt;
15  *
16  * {@img Ext.toolbar.Paging/Ext.toolbar.Paging.png Ext.toolbar.Paging component}
17  *
18  * &lt;p&gt;PagingToolbar is typically used as one of the Grid's toolbars:&lt;/p&gt;
19  * &lt;pre&gt;&lt;code&gt;
20  *    var itemsPerPage = 2;   // set the number of items you want per page
21  *    
22  *    var store = Ext.create('Ext.data.Store', {
23  *        id:'simpsonsStore',
24  *        autoLoad: false,
25  *        fields:['name', 'email', 'phone'],
26  *        pageSize: itemsPerPage, // items per page
27  *        proxy: {
28  *            type: 'ajax',
29  *            url: 'pagingstore.js',  // url that will load data with respect to start and limit params
30  *            reader: {
31  *                type: 'json',
32  *                root: 'items',
33  *                totalProperty: 'total'
34  *            }
35  *        }
36  *    });
37  *    
38  *    // specify segment of data you want to load using params
39  *    store.load({
40  *        params:{
41  *            start:0,    
42  *            limit: itemsPerPage
43  *        }
44  *    });
45  *    
46  *    Ext.create('Ext.grid.Panel', {
47  *        title: 'Simpsons',
48  *        store: store,
49  *        columns: [
50  *            {header: 'Name',  dataIndex: 'name'},
51  *            {header: 'Email', dataIndex: 'email', flex:1},
52  *            {header: 'Phone', dataIndex: 'phone'}
53  *        ],
54  *        width: 400,
55  *        height: 125,
56  *        dockedItems: [{
57  *            xtype: 'pagingtoolbar',
58  *            store: store,   // same store GridPanel is using
59  *            dock: 'bottom',
60  *            displayInfo: true
61  *        }],
62  *        renderTo: Ext.getBody()
63  *    });
64  * &lt;/code&gt;&lt;/pre&gt;
65  *
66  * &lt;p&gt;To use paging, pass the paging requirements to the server when the store is first loaded.&lt;/p&gt;
67  * &lt;pre&gt;&lt;code&gt;
68 store.load({
69     params: {
70         // specify params for the first page load if using paging
71         start: 0,          
72         limit: myPageSize,
73         // other params
74         foo:   'bar'
75     }
76 });
77  * &lt;/code&gt;&lt;/pre&gt;
78  * 
79  * &lt;p&gt;If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:&lt;/p&gt;
80  * &lt;pre&gt;&lt;code&gt;
81 var myStore = new Ext.data.Store({
82     {@link Ext.data.Store#autoLoad autoLoad}: {start: 0, limit: 25},
83     ...
84 });
85  * &lt;/code&gt;&lt;/pre&gt;
86  * 
87  * &lt;p&gt;The packet sent back from the server would have this form:&lt;/p&gt;
88  * &lt;pre&gt;&lt;code&gt;
89 {
90     &quot;success&quot;: true,
91     &quot;results&quot;: 2000, 
92     &quot;rows&quot;: [ // &lt;b&gt;*Note:&lt;/b&gt; this must be an Array 
93         { &quot;id&quot;:  1, &quot;name&quot;: &quot;Bill&quot;, &quot;occupation&quot;: &quot;Gardener&quot; },
94         { &quot;id&quot;:  2, &quot;name&quot;:  &quot;Ben&quot;, &quot;occupation&quot;: &quot;Horticulturalist&quot; },
95         ...
96         { &quot;id&quot;: 25, &quot;name&quot;:  &quot;Sue&quot;, &quot;occupation&quot;: &quot;Botanist&quot; }
97     ]
98 }
99  * &lt;/code&gt;&lt;/pre&gt;
100  * &lt;p&gt;&lt;u&gt;Paging with Local Data&lt;/u&gt;&lt;/p&gt;
101  * &lt;p&gt;Paging can also be accomplished with local data using extensions:&lt;/p&gt;
102  * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
103  * &lt;li&gt;&lt;a href=&quot;http://sencha.com/forum/showthread.php?t=71532&quot;&gt;Ext.ux.data.PagingStore&lt;/a&gt;&lt;/li&gt;
104  * &lt;li&gt;Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)&lt;/li&gt;
105  * &lt;/ul&gt;&lt;/div&gt;
106  * @constructor Create a new PagingToolbar
107  * @param {Object} config The config object
108  * @xtype pagingtoolbar
109  */
110 Ext.define('Ext.toolbar.Paging', {
111     extend: 'Ext.toolbar.Toolbar',
112     alias: 'widget.pagingtoolbar',
113     alternateClassName: 'Ext.PagingToolbar',
114     requires: ['Ext.toolbar.TextItem', 'Ext.form.field.Number'],
115 <span id='Ext-toolbar.Paging-cfg-store'>    /**
116 </span>     * @cfg {Ext.data.Store} store
117      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
118      */
119 <span id='Ext-toolbar.Paging-cfg-displayInfo'>    /**
120 </span>     * @cfg {Boolean} displayInfo
121      * &lt;tt&gt;true&lt;/tt&gt; to display the displayMsg (defaults to &lt;tt&gt;false&lt;/tt&gt;)
122      */
123     displayInfo: false,
124 <span id='Ext-toolbar.Paging-cfg-prependButtons'>    /**
125 </span>     * @cfg {Boolean} prependButtons
126      * &lt;tt&gt;true&lt;/tt&gt; to insert any configured &lt;tt&gt;items&lt;/tt&gt; &lt;i&gt;before&lt;/i&gt; the paging buttons.
127      * Defaults to &lt;tt&gt;false&lt;/tt&gt;.
128      */
129     prependButtons: false,
130 <span id='Ext-toolbar.Paging-cfg-displayMsg'>    /**
131 </span>     * @cfg {String} displayMsg
132      * The paging status message to display (defaults to &lt;tt&gt;'Displaying {0} - {1} of {2}'&lt;/tt&gt;).
133      * Note that this string is formatted using the braced numbers &lt;tt&gt;{0}-{2}&lt;/tt&gt; as tokens
134      * that are replaced by the values for start, end and total respectively. These tokens should
135      * be preserved when overriding this string if showing those values is desired.
136      */
137     displayMsg : 'Displaying {0} - {1} of {2}',
138 <span id='Ext-toolbar.Paging-cfg-emptyMsg'>    /**
139 </span>     * @cfg {String} emptyMsg
140      * The message to display when no records are found (defaults to 'No data to display')
141      */
142     emptyMsg : 'No data to display',
143 <span id='Ext-toolbar.Paging-cfg-beforePageText'>    /**
144 </span>     * @cfg {String} beforePageText
145      * The text displayed before the input item (defaults to &lt;tt&gt;'Page'&lt;/tt&gt;).
146      */
147     beforePageText : 'Page',
148 <span id='Ext-toolbar.Paging-cfg-afterPageText'>    /**
149 </span>     * @cfg {String} afterPageText
150      * Customizable piece of the default paging text (defaults to &lt;tt&gt;'of {0}'&lt;/tt&gt;). Note that
151      * this string is formatted using &lt;tt&gt;{0}&lt;/tt&gt; as a token that is replaced by the number of
152      * total pages. This token should be preserved when overriding this string if showing the
153      * total page count is desired.
154      */
155     afterPageText : 'of {0}',
156 <span id='Ext-toolbar.Paging-cfg-firstText'>    /**
157 </span>     * @cfg {String} firstText
158      * The quicktip text displayed for the first page button (defaults to &lt;tt&gt;'First Page'&lt;/tt&gt;).
159      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
160      */
161     firstText : 'First Page',
162 <span id='Ext-toolbar.Paging-cfg-prevText'>    /**
163 </span>     * @cfg {String} prevText
164      * The quicktip text displayed for the previous page button (defaults to &lt;tt&gt;'Previous Page'&lt;/tt&gt;).
165      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
166      */
167     prevText : 'Previous Page',
168 <span id='Ext-toolbar.Paging-cfg-nextText'>    /**
169 </span>     * @cfg {String} nextText
170      * The quicktip text displayed for the next page button (defaults to &lt;tt&gt;'Next Page'&lt;/tt&gt;).
171      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
172      */
173     nextText : 'Next Page',
174 <span id='Ext-toolbar.Paging-cfg-lastText'>    /**
175 </span>     * @cfg {String} lastText
176      * The quicktip text displayed for the last page button (defaults to &lt;tt&gt;'Last Page'&lt;/tt&gt;).
177      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
178      */
179     lastText : 'Last Page',
180 <span id='Ext-toolbar.Paging-cfg-refreshText'>    /**
181 </span>     * @cfg {String} refreshText
182      * The quicktip text displayed for the Refresh button (defaults to &lt;tt&gt;'Refresh'&lt;/tt&gt;).
183      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
184      */
185     refreshText : 'Refresh',
186 <span id='Ext-toolbar.Paging-cfg-inputItemWidth'>    /**
187 </span>     * @cfg {Number} inputItemWidth
188      * The width in pixels of the input field used to display and change the current page number (defaults to 30).
189      */
190     inputItemWidth : 30,
191     
192 <span id='Ext-toolbar.Paging-method-getPagingItems'>    /**
193 </span>     * Gets the standard paging items in the toolbar
194      * @private
195      */
196     getPagingItems: function() {
197         var me = this;
198         
199         return [{
200             itemId: 'first',
201             tooltip: me.firstText,
202             overflowText: me.firstText,
203             iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
204             disabled: true,
205             handler: me.moveFirst,
206             scope: me
207         },{
208             itemId: 'prev',
209             tooltip: me.prevText,
210             overflowText: me.prevText,
211             iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
212             disabled: true,
213             handler: me.movePrevious,
214             scope: me
215         },
216         '-',
217         me.beforePageText,
218         {
219             xtype: 'numberfield',
220             itemId: 'inputItem',
221             name: 'inputItem',
222             cls: Ext.baseCSSPrefix + 'tbar-page-number',
223             allowDecimals: false,
224             minValue: 1,
225             hideTrigger: true,
226             enableKeyEvents: true,
227             selectOnFocus: true,
228             submitValue: false,
229             width: me.inputItemWidth,
230             margins: '-1 2 3 2',
231             listeners: {
232                 scope: me,
233                 keydown: me.onPagingKeyDown,
234                 blur: me.onPagingBlur
235             }
236         },{
237             xtype: 'tbtext',
238             itemId: 'afterTextItem',
239             text: Ext.String.format(me.afterPageText, 1)
240         },
241         '-',
242         {
243             itemId: 'next',
244             tooltip: me.nextText,
245             overflowText: me.nextText,
246             iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
247             disabled: true,
248             handler: me.moveNext,
249             scope: me
250         },{
251             itemId: 'last',
252             tooltip: me.lastText,
253             overflowText: me.lastText,
254             iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
255             disabled: true,
256             handler: me.moveLast,
257             scope: me
258         },
259         '-',
260         {
261             itemId: 'refresh',
262             tooltip: me.refreshText,
263             overflowText: me.refreshText,
264             iconCls: Ext.baseCSSPrefix + 'tbar-loading',
265             handler: me.doRefresh,
266             scope: me
267         }];
268     },
269
270     initComponent : function(){
271         var me = this,
272             pagingItems = me.getPagingItems(),
273             userItems   = me.items || me.buttons || [];
274             
275         if (me.prependButtons) {
276             me.items = userItems.concat(pagingItems);
277         } else {
278             me.items = pagingItems.concat(userItems);
279         }
280         delete me.buttons;
281         
282         if (me.displayInfo) {
283             me.items.push('-&gt;');
284             me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
285         }
286         
287         me.callParent();
288         
289         me.addEvents(
290 <span id='Ext-toolbar.Paging-event-change'>            /**
291 </span>             * @event change
292              * Fires after the active page has been changed.
293              * @param {Ext.toolbar.Paging} this
294              * @param {Object} pageData An object that has these properties:&lt;ul&gt;
295              * &lt;li&gt;&lt;code&gt;total&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The total number of records in the dataset as
296              * returned by the server&lt;/div&gt;&lt;/li&gt;
297              * &lt;li&gt;&lt;code&gt;currentPage&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The current page number&lt;/div&gt;&lt;/li&gt;
298              * &lt;li&gt;&lt;code&gt;pageCount&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The total number of pages (calculated from
299              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})&lt;/div&gt;&lt;/li&gt;
300              * &lt;li&gt;&lt;code&gt;toRecord&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The starting record index for the current page&lt;/div&gt;&lt;/li&gt;
301              * &lt;li&gt;&lt;code&gt;fromRecord&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The ending record index for the current page&lt;/div&gt;&lt;/li&gt;
302              * &lt;/ul&gt;
303              */
304             'change',
305 <span id='Ext-toolbar.Paging-event-beforechange'>            /**
306 </span>             * @event beforechange
307              * Fires just before the active page is changed.
308              * Return false to prevent the active page from being changed.
309              * @param {Ext.toolbar.Paging} this
310              * @param {Number} page The page number that will be loaded on change 
311              */
312             'beforechange'
313         );
314         me.on('afterlayout', me.onLoad, me, {single: true});
315
316         me.bindStore(me.store, true);
317     },
318     // private
319     updateInfo : function(){
320         var me = this,
321             displayItem = me.child('#displayItem'),
322             store = me.store,
323             pageData = me.getPageData(),
324             count, msg;
325
326         if (displayItem) {
327             count = store.getCount();
328             if (count === 0) {
329                 msg = me.emptyMsg;
330             } else {
331                 msg = Ext.String.format(
332                     me.displayMsg,
333                     pageData.fromRecord,
334                     pageData.toRecord,
335                     pageData.total
336                 );
337             }
338             displayItem.setText(msg);
339             me.doComponentLayout();
340         }
341     },
342
343     // private
344     onLoad : function(){
345         var me = this,
346             pageData,
347             currPage,
348             pageCount,
349             afterText;
350             
351         if (!me.rendered) {
352             return;
353         }
354
355         pageData = me.getPageData();
356         currPage = pageData.currentPage;
357         pageCount = pageData.pageCount;
358         afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
359
360         me.child('#afterTextItem').setText(afterText);
361         me.child('#inputItem').setValue(currPage);
362         me.child('#first').setDisabled(currPage === 1);
363         me.child('#prev').setDisabled(currPage === 1);
364         me.child('#next').setDisabled(currPage === pageCount);
365         me.child('#last').setDisabled(currPage === pageCount);
366         me.child('#refresh').enable();
367         me.updateInfo();
368         me.fireEvent('change', me, pageData);
369     },
370
371     // private
372     getPageData : function(){
373         var store = this.store,
374             totalCount = store.getTotalCount();
375             
376         return {
377             total : totalCount,
378             currentPage : store.currentPage,
379             pageCount: Math.ceil(totalCount / store.pageSize),
380             //pageCount :  store.getPageCount(),
381             fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
382             toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
383             
384         };
385     },
386
387     // private
388     onLoadError : function(){
389         if (!this.rendered) {
390             return;
391         }
392         this.child('#refresh').enable();
393     },
394
395     // private
396     readPageFromInput : function(pageData){
397         var v = this.child('#inputItem').getValue(),
398             pageNum = parseInt(v, 10);
399             
400         if (!v || isNaN(pageNum)) {
401             this.child('#inputItem').setValue(pageData.currentPage);
402             return false;
403         }
404         return pageNum;
405     },
406
407     onPagingFocus : function(){
408         this.child('#inputItem').select();
409     },
410
411     //private
412     onPagingBlur : function(e){
413         var curPage = this.getPageData().currentPage;
414         this.child('#inputItem').setValue(curPage);
415     },
416
417     // private
418     onPagingKeyDown : function(field, e){
419         var k = e.getKey(),
420             pageData = this.getPageData(),
421             increment = e.shiftKey ? 10 : 1,
422             pageNum,
423             me = this;
424
425         if (k == e.RETURN) {
426             e.stopEvent();
427             pageNum = me.readPageFromInput(pageData);
428             if (pageNum !== false) {
429                 pageNum = Math.min(Math.max(1, pageNum), pageData.total);
430                 if(me.fireEvent('beforechange', me, pageNum) !== false){
431                     me.store.loadPage(pageNum);
432                 }
433             }
434         } else if (k == e.HOME || k == e.END) {
435             e.stopEvent();
436             pageNum = k == e.HOME ? 1 : pageData.pageCount;
437             field.setValue(pageNum);
438         } else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN) {
439             e.stopEvent();
440             pageNum = me.readPageFromInput(pageData);
441             if (pageNum) {
442                 if (k == e.DOWN || k == e.PAGEDOWN) {
443                     increment *= -1;
444                 }
445                 pageNum += increment;
446                 if (pageNum &gt;= 1 &amp;&amp; pageNum &lt;= pageData.pages) {
447                     field.setValue(pageNum);
448                 }
449             }
450         }
451     },
452
453     // private
454     beforeLoad : function(){
455         if(this.rendered &amp;&amp; this.refresh){
456             this.refresh.disable();
457         }
458     },
459
460     // private
461     doLoad : function(start){
462         if(this.fireEvent('beforechange', this, o) !== false){
463             this.store.load();
464         }
465     },
466
467 <span id='Ext-toolbar.Paging-method-moveFirst'>    /**
468 </span>     * Move to the first page, has the same effect as clicking the 'first' button.
469      */
470     moveFirst : function(){
471         var me = this;
472         if(me.fireEvent('beforechange', me, 1) !== false){
473             me.store.loadPage(1);
474         }
475     },
476
477 <span id='Ext-toolbar.Paging-method-movePrevious'>    /**
478 </span>     * Move to the previous page, has the same effect as clicking the 'previous' button.
479      */
480     movePrevious : function(){
481         var me = this,
482             prev = me.store.currentPage - 1;
483         
484         if(me.fireEvent('beforechange', me, prev) !== false){
485             me.store.previousPage();
486         }
487     },
488
489 <span id='Ext-toolbar.Paging-method-moveNext'>    /**
490 </span>     * Move to the next page, has the same effect as clicking the 'next' button.
491      */
492     moveNext : function(){
493         var me = this;        
494         if(me.fireEvent('beforechange', me, me.store.currentPage + 1) !== false){
495             me.store.nextPage();
496         }
497     },
498
499 <span id='Ext-toolbar.Paging-method-moveLast'>    /**
500 </span>     * Move to the last page, has the same effect as clicking the 'last' button.
501      */
502     moveLast : function(){
503         var me = this, 
504             last = this.getPageData().pageCount;
505         
506         if(me.fireEvent('beforechange', me, last) !== false){
507             me.store.loadPage(last);
508         }
509     },
510
511 <span id='Ext-toolbar.Paging-method-doRefresh'>    /**
512 </span>     * Refresh the current page, has the same effect as clicking the 'refresh' button.
513      */
514     doRefresh : function(){
515         var me = this,
516             current = me.store.currentPage;
517         
518         if(me.fireEvent('beforechange', me, current) !== false){
519             me.store.loadPage(current);
520         }
521     },
522
523 <span id='Ext-toolbar.Paging-method-bindStore'>    /**
524 </span>     * Binds the paging toolbar to the specified {@link Ext.data.Store}
525      * @param {Store} store The store to bind to this toolbar
526      * @param {Boolean} initial (Optional) true to not remove listeners
527      */
528     bindStore : function(store, initial){
529         var me = this;
530         
531         if (!initial &amp;&amp; me.store) {
532             if(store !== me.store &amp;&amp; me.store.autoDestroy){
533                 me.store.destroy();
534             }else{
535                 me.store.un('beforeload', me.beforeLoad, me);
536                 me.store.un('load', me.onLoad, me);
537                 me.store.un('exception', me.onLoadError, me);
538             }
539             if(!store){
540                 me.store = null;
541             }
542         }
543         if (store) {
544             store = Ext.data.StoreManager.lookup(store);
545             store.on({
546                 scope: me,
547                 beforeload: me.beforeLoad,
548                 load: me.onLoad,
549                 exception: me.onLoadError
550             });
551         }
552         me.store = store;
553     },
554
555 <span id='Ext-toolbar.Paging-method-unbind'>    /**
556 </span>     * Unbinds the paging toolbar from the specified {@link Ext.data.Store} &lt;b&gt;(deprecated)&lt;/b&gt;
557      * @param {Ext.data.Store} store The data store to unbind
558      */
559     unbind : function(store){
560         this.bindStore(null);
561     },
562
563 <span id='Ext-toolbar.Paging-method-bind'>    /**
564 </span>     * Binds the paging toolbar to the specified {@link Ext.data.Store} &lt;b&gt;(deprecated)&lt;/b&gt;
565      * @param {Ext.data.Store} store The data store to bind
566      */
567     bind : function(store){
568         this.bindStore(store);
569     },
570
571     // private
572     onDestroy : function(){
573         this.bindStore(null);
574         this.callParent();
575     }
576 });
577 </pre></pre></body></html>