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