Upgrade to ExtJS 4.0.2 - Released 06/09/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'>/**
19 </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  */
124 Ext.define('Ext.toolbar.Paging', {
125     extend: 'Ext.toolbar.Toolbar',
126     alias: 'widget.pagingtoolbar',
127     alternateClassName: 'Ext.PagingToolbar',
128     requires: ['Ext.toolbar.TextItem', 'Ext.form.field.Number'],
129 <span id='Ext-toolbar-Paging-cfg-store'>    /**
130 </span>     * @cfg {Ext.data.Store} store
131      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
132      */
133 <span id='Ext-toolbar-Paging-cfg-displayInfo'>    /**
134 </span>     * @cfg {Boolean} displayInfo
135      * &lt;tt&gt;true&lt;/tt&gt; to display the displayMsg (defaults to &lt;tt&gt;false&lt;/tt&gt;)
136      */
137     displayInfo: false,
138 <span id='Ext-toolbar-Paging-cfg-prependButtons'>    /**
139 </span>     * @cfg {Boolean} prependButtons
140      * &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.
141      * Defaults to &lt;tt&gt;false&lt;/tt&gt;.
142      */
143     prependButtons: false,
144 <span id='Ext-toolbar-Paging-cfg-displayMsg'>    /**
145 </span>     * @cfg {String} displayMsg
146      * The paging status message to display (defaults to &lt;tt&gt;'Displaying {0} - {1} of {2}'&lt;/tt&gt;).
147      * Note that this string is formatted using the braced numbers &lt;tt&gt;{0}-{2}&lt;/tt&gt; as tokens
148      * that are replaced by the values for start, end and total respectively. These tokens should
149      * be preserved when overriding this string if showing those values is desired.
150      */
151     displayMsg : 'Displaying {0} - {1} of {2}',
152 <span id='Ext-toolbar-Paging-cfg-emptyMsg'>    /**
153 </span>     * @cfg {String} emptyMsg
154      * The message to display when no records are found (defaults to 'No data to display')
155      */
156     emptyMsg : 'No data to display',
157 <span id='Ext-toolbar-Paging-cfg-beforePageText'>    /**
158 </span>     * @cfg {String} beforePageText
159      * The text displayed before the input item (defaults to &lt;tt&gt;'Page'&lt;/tt&gt;).
160      */
161     beforePageText : 'Page',
162 <span id='Ext-toolbar-Paging-cfg-afterPageText'>    /**
163 </span>     * @cfg {String} afterPageText
164      * Customizable piece of the default paging text (defaults to &lt;tt&gt;'of {0}'&lt;/tt&gt;). Note that
165      * this string is formatted using &lt;tt&gt;{0}&lt;/tt&gt; as a token that is replaced by the number of
166      * total pages. This token should be preserved when overriding this string if showing the
167      * total page count is desired.
168      */
169     afterPageText : 'of {0}',
170 <span id='Ext-toolbar-Paging-cfg-firstText'>    /**
171 </span>     * @cfg {String} firstText
172      * The quicktip text displayed for the first page button (defaults to &lt;tt&gt;'First Page'&lt;/tt&gt;).
173      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
174      */
175     firstText : 'First Page',
176 <span id='Ext-toolbar-Paging-cfg-prevText'>    /**
177 </span>     * @cfg {String} prevText
178      * The quicktip text displayed for the previous page button (defaults to &lt;tt&gt;'Previous Page'&lt;/tt&gt;).
179      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
180      */
181     prevText : 'Previous Page',
182 <span id='Ext-toolbar-Paging-cfg-nextText'>    /**
183 </span>     * @cfg {String} nextText
184      * The quicktip text displayed for the next page button (defaults to &lt;tt&gt;'Next Page'&lt;/tt&gt;).
185      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
186      */
187     nextText : 'Next Page',
188 <span id='Ext-toolbar-Paging-cfg-lastText'>    /**
189 </span>     * @cfg {String} lastText
190      * The quicktip text displayed for the last page button (defaults to &lt;tt&gt;'Last Page'&lt;/tt&gt;).
191      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
192      */
193     lastText : 'Last Page',
194 <span id='Ext-toolbar-Paging-cfg-refreshText'>    /**
195 </span>     * @cfg {String} refreshText
196      * The quicktip text displayed for the Refresh button (defaults to &lt;tt&gt;'Refresh'&lt;/tt&gt;).
197      * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
198      */
199     refreshText : 'Refresh',
200 <span id='Ext-toolbar-Paging-cfg-inputItemWidth'>    /**
201 </span>     * @cfg {Number} inputItemWidth
202      * The width in pixels of the input field used to display and change the current page number (defaults to 30).
203      */
204     inputItemWidth : 30,
205     
206 <span id='Ext-toolbar-Paging-method-getPagingItems'>    /**
207 </span>     * Gets the standard paging items in the toolbar
208      * @private
209      */
210     getPagingItems: function() {
211         var me = this;
212         
213         return [{
214             itemId: 'first',
215             tooltip: me.firstText,
216             overflowText: me.firstText,
217             iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
218             disabled: true,
219             handler: me.moveFirst,
220             scope: me
221         },{
222             itemId: 'prev',
223             tooltip: me.prevText,
224             overflowText: me.prevText,
225             iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
226             disabled: true,
227             handler: me.movePrevious,
228             scope: me
229         },
230         '-',
231         me.beforePageText,
232         {
233             xtype: 'numberfield',
234             itemId: 'inputItem',
235             name: 'inputItem',
236             cls: Ext.baseCSSPrefix + 'tbar-page-number',
237             allowDecimals: false,
238             minValue: 1,
239             hideTrigger: true,
240             enableKeyEvents: true,
241             selectOnFocus: true,
242             submitValue: false,
243             width: me.inputItemWidth,
244             margins: '-1 2 3 2',
245             listeners: {
246                 scope: me,
247                 keydown: me.onPagingKeyDown,
248                 blur: me.onPagingBlur
249             }
250         },{
251             xtype: 'tbtext',
252             itemId: 'afterTextItem',
253             text: Ext.String.format(me.afterPageText, 1)
254         },
255         '-',
256         {
257             itemId: 'next',
258             tooltip: me.nextText,
259             overflowText: me.nextText,
260             iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
261             disabled: true,
262             handler: me.moveNext,
263             scope: me
264         },{
265             itemId: 'last',
266             tooltip: me.lastText,
267             overflowText: me.lastText,
268             iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
269             disabled: true,
270             handler: me.moveLast,
271             scope: me
272         },
273         '-',
274         {
275             itemId: 'refresh',
276             tooltip: me.refreshText,
277             overflowText: me.refreshText,
278             iconCls: Ext.baseCSSPrefix + 'tbar-loading',
279             handler: me.doRefresh,
280             scope: me
281         }];
282     },
283
284     initComponent : function(){
285         var me = this,
286             pagingItems = me.getPagingItems(),
287             userItems   = me.items || me.buttons || [];
288             
289         if (me.prependButtons) {
290             me.items = userItems.concat(pagingItems);
291         } else {
292             me.items = pagingItems.concat(userItems);
293         }
294         delete me.buttons;
295         
296         if (me.displayInfo) {
297             me.items.push('-&gt;');
298             me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
299         }
300         
301         me.callParent();
302         
303         me.addEvents(
304 <span id='Ext-toolbar-Paging-event-change'>            /**
305 </span>             * @event change
306              * Fires after the active page has been changed.
307              * @param {Ext.toolbar.Paging} this
308              * @param {Object} pageData An object that has these properties:&lt;ul&gt;
309              * &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
310              * returned by the server&lt;/div&gt;&lt;/li&gt;
311              * &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;
312              * &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
313              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})&lt;/div&gt;&lt;/li&gt;
314              * &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;
315              * &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;
316              * &lt;/ul&gt;
317              */
318             'change',
319 <span id='Ext-toolbar-Paging-event-beforechange'>            /**
320 </span>             * @event beforechange
321              * Fires just before the active page is changed.
322              * Return false to prevent the active page from being changed.
323              * @param {Ext.toolbar.Paging} this
324              * @param {Number} page The page number that will be loaded on change 
325              */
326             'beforechange'
327         );
328         me.on('afterlayout', me.onLoad, me, {single: true});
329
330         me.bindStore(me.store, true);
331     },
332     // private
333     updateInfo : function(){
334         var me = this,
335             displayItem = me.child('#displayItem'),
336             store = me.store,
337             pageData = me.getPageData(),
338             count, msg;
339
340         if (displayItem) {
341             count = store.getCount();
342             if (count === 0) {
343                 msg = me.emptyMsg;
344             } else {
345                 msg = Ext.String.format(
346                     me.displayMsg,
347                     pageData.fromRecord,
348                     pageData.toRecord,
349                     pageData.total
350                 );
351             }
352             displayItem.setText(msg);
353             me.doComponentLayout();
354         }
355     },
356
357     // private
358     onLoad : function(){
359         var me = this,
360             pageData,
361             currPage,
362             pageCount,
363             afterText;
364             
365         if (!me.rendered) {
366             return;
367         }
368
369         pageData = me.getPageData();
370         currPage = pageData.currentPage;
371         pageCount = pageData.pageCount;
372         afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
373
374         me.child('#afterTextItem').setText(afterText);
375         me.child('#inputItem').setValue(currPage);
376         me.child('#first').setDisabled(currPage === 1);
377         me.child('#prev').setDisabled(currPage === 1);
378         me.child('#next').setDisabled(currPage === pageCount);
379         me.child('#last').setDisabled(currPage === pageCount);
380         me.child('#refresh').enable();
381         me.updateInfo();
382         me.fireEvent('change', me, pageData);
383     },
384
385     // private
386     getPageData : function(){
387         var store = this.store,
388             totalCount = store.getTotalCount();
389             
390         return {
391             total : totalCount,
392             currentPage : store.currentPage,
393             pageCount: Math.ceil(totalCount / store.pageSize),
394             fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
395             toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
396             
397         };
398     },
399
400     // private
401     onLoadError : function(){
402         if (!this.rendered) {
403             return;
404         }
405         this.child('#refresh').enable();
406     },
407
408     // private
409     readPageFromInput : function(pageData){
410         var v = this.child('#inputItem').getValue(),
411             pageNum = parseInt(v, 10);
412             
413         if (!v || isNaN(pageNum)) {
414             this.child('#inputItem').setValue(pageData.currentPage);
415             return false;
416         }
417         return pageNum;
418     },
419
420     onPagingFocus : function(){
421         this.child('#inputItem').select();
422     },
423
424     //private
425     onPagingBlur : function(e){
426         var curPage = this.getPageData().currentPage;
427         this.child('#inputItem').setValue(curPage);
428     },
429
430     // private
431     onPagingKeyDown : function(field, e){
432         var me = this,
433             k = e.getKey(),
434             pageData = me.getPageData(),
435             increment = e.shiftKey ? 10 : 1,
436             pageNum;
437
438         if (k == e.RETURN) {
439             e.stopEvent();
440             pageNum = me.readPageFromInput(pageData);
441             if (pageNum !== false) {
442                 pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount);
443                 if(me.fireEvent('beforechange', me, pageNum) !== false){
444                     me.store.loadPage(pageNum);
445                 }
446             }
447         } else if (k == e.HOME || k == e.END) {
448             e.stopEvent();
449             pageNum = k == e.HOME ? 1 : pageData.pageCount;
450             field.setValue(pageNum);
451         } else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN) {
452             e.stopEvent();
453             pageNum = me.readPageFromInput(pageData);
454             if (pageNum) {
455                 if (k == e.DOWN || k == e.PAGEDOWN) {
456                     increment *= -1;
457                 }
458                 pageNum += increment;
459                 if (pageNum &gt;= 1 &amp;&amp; pageNum &lt;= pageData.pages) {
460                     field.setValue(pageNum);
461                 }
462             }
463         }
464     },
465
466     // private
467     beforeLoad : function(){
468         if(this.rendered &amp;&amp; this.refresh){
469             this.refresh.disable();
470         }
471     },
472
473     // private
474     doLoad : function(start){
475         if(this.fireEvent('beforechange', this, o) !== false){
476             this.store.load();
477         }
478     },
479
480 <span id='Ext-toolbar-Paging-method-moveFirst'>    /**
481 </span>     * Move to the first page, has the same effect as clicking the 'first' button.
482      */
483     moveFirst : function(){
484         if (this.fireEvent('beforechange', this, 1) !== false){
485             this.store.loadPage(1);
486         }
487     },
488
489 <span id='Ext-toolbar-Paging-method-movePrevious'>    /**
490 </span>     * Move to the previous page, has the same effect as clicking the 'previous' button.
491      */
492     movePrevious : function(){
493         var me = this,
494             prev = me.store.currentPage - 1;
495         
496         if (prev &gt; 0) {
497             if (me.fireEvent('beforechange', me, prev) !== false) {
498                 me.store.previousPage();
499             }
500         }
501     },
502
503 <span id='Ext-toolbar-Paging-method-moveNext'>    /**
504 </span>     * Move to the next page, has the same effect as clicking the 'next' button.
505      */
506     moveNext : function(){
507         var me = this,
508             total = me.getPageData().pageCount,
509             next = me.store.currentPage + 1;
510                
511         if (next &lt;= total) {
512             if (me.fireEvent('beforechange', me, next) !== false) {
513                 me.store.nextPage();
514             }
515         }
516     },
517
518 <span id='Ext-toolbar-Paging-method-moveLast'>    /**
519 </span>     * Move to the last page, has the same effect as clicking the 'last' button.
520      */
521     moveLast : function(){
522         var me = this, 
523             last = me.getPageData().pageCount;
524         
525         if (me.fireEvent('beforechange', me, last) !== false) {
526             me.store.loadPage(last);
527         }
528     },
529
530 <span id='Ext-toolbar-Paging-method-doRefresh'>    /**
531 </span>     * Refresh the current page, has the same effect as clicking the 'refresh' button.
532      */
533     doRefresh : function(){
534         var me = this,
535             current = me.store.currentPage;
536         
537         if (me.fireEvent('beforechange', me, current) !== false) {
538             me.store.loadPage(current);
539         }
540     },
541
542 <span id='Ext-toolbar-Paging-method-bindStore'>    /**
543 </span>     * Binds the paging toolbar to the specified {@link Ext.data.Store}
544      * @param {Store} store The store to bind to this toolbar
545      * @param {Boolean} initial (Optional) true to not remove listeners
546      */
547     bindStore : function(store, initial){
548         var me = this;
549         
550         if (!initial &amp;&amp; me.store) {
551             if(store !== me.store &amp;&amp; me.store.autoDestroy){
552                 me.store.destroy();
553             }else{
554                 me.store.un('beforeload', me.beforeLoad, me);
555                 me.store.un('load', me.onLoad, me);
556                 me.store.un('exception', me.onLoadError, me);
557             }
558             if(!store){
559                 me.store = null;
560             }
561         }
562         if (store) {
563             store = Ext.data.StoreManager.lookup(store);
564             store.on({
565                 scope: me,
566                 beforeload: me.beforeLoad,
567                 load: me.onLoad,
568                 exception: me.onLoadError
569             });
570         }
571         me.store = store;
572     },
573
574 <span id='Ext-toolbar-Paging-method-unbind'>    /**
575 </span>     * Unbinds the paging toolbar from the specified {@link Ext.data.Store} &lt;b&gt;(deprecated)&lt;/b&gt;
576      * @param {Ext.data.Store} store The data store to unbind
577      */
578     unbind : function(store){
579         this.bindStore(null);
580     },
581
582 <span id='Ext-toolbar-Paging-method-bind'>    /**
583 </span>     * Binds the paging toolbar to the specified {@link Ext.data.Store} &lt;b&gt;(deprecated)&lt;/b&gt;
584      * @param {Ext.data.Store} store The data store to bind
585      */
586     bind : function(store){
587         this.bindStore(store);
588     },
589
590     // private
591     onDestroy : function(){
592         this.bindStore(null);
593         this.callParent();
594     }
595 });
596 </pre>
597 </body>
598 </html>