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