3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.toolbar.Paging
17 * @extends Ext.toolbar.Toolbar
18 * <p>As the amount of records increases, the time required for the browser to render
19 * them increases. Paging is used to reduce the amount of data exchanged with the client.
20 * Note: if there are more records/rows than can be viewed in the available screen area, vertical
21 * scrollbars will be added.</p>
22 * <p>Paging is typically handled on the server side (see exception below). The client sends
23 * parameters to the server side, which the server needs to interpret and then respond with the
24 * appropriate data.</p>
25 * <p><b>Ext.toolbar.Paging</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
26 * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
27 * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
28 * paging criteria.</p>
30 * {@img Ext.toolbar.Paging/Ext.toolbar.Paging.png Ext.toolbar.Paging component}
32 * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
34 * var itemsPerPage = 2; // set the number of items you want per page
36 * var store = Ext.create('Ext.data.Store', {
39 * fields:['name', 'email', 'phone'],
40 * pageSize: itemsPerPage, // items per page
43 * url: 'pagingstore.js', // url that will load data with respect to start and limit params
47 * totalProperty: 'total'
52 * // specify segment of data you want to load using params
60 * Ext.create('Ext.grid.Panel', {
64 * {header: 'Name', dataIndex: 'name'},
65 * {header: 'Email', dataIndex: 'email', flex:1},
66 * {header: 'Phone', dataIndex: 'phone'}
71 * xtype: 'pagingtoolbar',
72 * store: store, // same store GridPanel is using
76 * renderTo: Ext.getBody()
80 * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
84 // specify params for the first page load if using paging
93 * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
95 var myStore = new Ext.data.Store({
96 {@link Ext.data.Store#autoLoad autoLoad}: {start: 0, limit: 25},
101 * <p>The packet sent back from the server would have this form:</p>
106 "rows": [ // <b>*Note:</b> this must be an Array
107 { "id": 1, "name": "Bill", "occupation": "Gardener" },
108 { "id": 2, "name": "Ben", "occupation": "Horticulturalist" },
110 { "id": 25, "name": "Sue", "occupation": "Botanist" }
114 * <p><u>Paging with Local Data</u></p>
115 * <p>Paging can also be accomplished with local data using extensions:</p>
116 * <div class="mdetail-params"><ul>
117 * <li><a href="http://sencha.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
118 * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
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'],
127 * @cfg {Ext.data.Store} store
128 * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
131 * @cfg {Boolean} displayInfo
132 * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
136 * @cfg {Boolean} prependButtons
137 * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
138 * Defaults to <tt>false</tt>.
140 prependButtons: false,
142 * @cfg {String} displayMsg
143 * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
144 * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
145 * that are replaced by the values for start, end and total respectively. These tokens should
146 * be preserved when overriding this string if showing those values is desired.
148 displayMsg : 'Displaying {0} - {1} of {2}',
150 * @cfg {String} emptyMsg
151 * The message to display when no records are found (defaults to 'No data to display')
153 emptyMsg : 'No data to display',
155 * @cfg {String} beforePageText
156 * The text displayed before the input item (defaults to <tt>'Page'</tt>).
158 beforePageText : 'Page',
160 * @cfg {String} afterPageText
161 * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
162 * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
163 * total pages. This token should be preserved when overriding this string if showing the
164 * total page count is desired.
166 afterPageText : 'of {0}',
168 * @cfg {String} firstText
169 * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
170 * <b>Note</b>: quick tips must be initialized for the quicktip to show.
172 firstText : 'First Page',
174 * @cfg {String} prevText
175 * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
176 * <b>Note</b>: quick tips must be initialized for the quicktip to show.
178 prevText : 'Previous Page',
180 * @cfg {String} nextText
181 * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
182 * <b>Note</b>: quick tips must be initialized for the quicktip to show.
184 nextText : 'Next Page',
186 * @cfg {String} lastText
187 * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
188 * <b>Note</b>: quick tips must be initialized for the quicktip to show.
190 lastText : 'Last Page',
192 * @cfg {String} refreshText
193 * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
194 * <b>Note</b>: quick tips must be initialized for the quicktip to show.
196 refreshText : 'Refresh',
198 * @cfg {Number} inputItemWidth
199 * The width in pixels of the input field used to display and change the current page number (defaults to 30).
204 * Gets the standard paging items in the toolbar
207 getPagingItems: function() {
212 tooltip: me.firstText,
213 overflowText: me.firstText,
214 iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
216 handler: me.moveFirst,
220 tooltip: me.prevText,
221 overflowText: me.prevText,
222 iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
224 handler: me.movePrevious,
230 xtype: 'numberfield',
233 cls: Ext.baseCSSPrefix + 'tbar-page-number',
234 allowDecimals: false,
237 enableKeyEvents: true,
240 width: me.inputItemWidth,
244 keydown: me.onPagingKeyDown,
245 blur: me.onPagingBlur
249 itemId: 'afterTextItem',
250 text: Ext.String.format(me.afterPageText, 1)
255 tooltip: me.nextText,
256 overflowText: me.nextText,
257 iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
259 handler: me.moveNext,
263 tooltip: me.lastText,
264 overflowText: me.lastText,
265 iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
267 handler: me.moveLast,
273 tooltip: me.refreshText,
274 overflowText: me.refreshText,
275 iconCls: Ext.baseCSSPrefix + 'tbar-loading',
276 handler: me.doRefresh,
281 initComponent : function(){
283 pagingItems = me.getPagingItems(),
284 userItems = me.items || me.buttons || [];
286 if (me.prependButtons) {
287 me.items = userItems.concat(pagingItems);
289 me.items = pagingItems.concat(userItems);
293 if (me.displayInfo) {
295 me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
303 * Fires after the active page has been changed.
304 * @param {Ext.toolbar.Paging} this
305 * @param {Object} pageData An object that has these properties:<ul>
306 * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
307 * returned by the server</div></li>
308 * <li><code>currentPage</code> : Number <div class="sub-desc">The current page number</div></li>
309 * <li><code>pageCount</code> : Number <div class="sub-desc">The total number of pages (calculated from
310 * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
311 * <li><code>toRecord</code> : Number <div class="sub-desc">The starting record index for the current page</div></li>
312 * <li><code>fromRecord</code> : Number <div class="sub-desc">The ending record index for the current page</div></li>
317 * @event beforechange
318 * Fires just before the active page is changed.
319 * Return false to prevent the active page from being changed.
320 * @param {Ext.toolbar.Paging} this
321 * @param {Number} page The page number that will be loaded on change
325 me.on('afterlayout', me.onLoad, me, {single: true});
327 me.bindStore(me.store, true);
330 updateInfo : function(){
332 displayItem = me.child('#displayItem'),
334 pageData = me.getPageData(),
338 count = store.getCount();
342 msg = Ext.String.format(
349 displayItem.setText(msg);
350 me.doComponentLayout();
366 pageData = me.getPageData();
367 currPage = pageData.currentPage;
368 pageCount = pageData.pageCount;
369 afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
371 me.child('#afterTextItem').setText(afterText);
372 me.child('#inputItem').setValue(currPage);
373 me.child('#first').setDisabled(currPage === 1);
374 me.child('#prev').setDisabled(currPage === 1);
375 me.child('#next').setDisabled(currPage === pageCount);
376 me.child('#last').setDisabled(currPage === pageCount);
377 me.child('#refresh').enable();
379 me.fireEvent('change', me, pageData);
383 getPageData : function(){
384 var store = this.store,
385 totalCount = store.getTotalCount();
389 currentPage : store.currentPage,
390 pageCount: Math.ceil(totalCount / store.pageSize),
391 fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
392 toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
398 onLoadError : function(){
399 if (!this.rendered) {
402 this.child('#refresh').enable();
406 readPageFromInput : function(pageData){
407 var v = this.child('#inputItem').getValue(),
408 pageNum = parseInt(v, 10);
410 if (!v || isNaN(pageNum)) {
411 this.child('#inputItem').setValue(pageData.currentPage);
417 onPagingFocus : function(){
418 this.child('#inputItem').select();
422 onPagingBlur : function(e){
423 var curPage = this.getPageData().currentPage;
424 this.child('#inputItem').setValue(curPage);
428 onPagingKeyDown : function(field, e){
431 pageData = me.getPageData(),
432 increment = e.shiftKey ? 10 : 1,
437 pageNum = me.readPageFromInput(pageData);
438 if (pageNum !== false) {
439 pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount);
440 if(me.fireEvent('beforechange', me, pageNum) !== false){
441 me.store.loadPage(pageNum);
444 } else if (k == e.HOME || k == e.END) {
446 pageNum = k == e.HOME ? 1 : pageData.pageCount;
447 field.setValue(pageNum);
448 } else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN) {
450 pageNum = me.readPageFromInput(pageData);
452 if (k == e.DOWN || k == e.PAGEDOWN) {
455 pageNum += increment;
456 if (pageNum >= 1 && pageNum <= pageData.pages) {
457 field.setValue(pageNum);
464 beforeLoad : function(){
465 if(this.rendered && this.refresh){
466 this.refresh.disable();
471 doLoad : function(start){
472 if(this.fireEvent('beforechange', this, o) !== false){
478 * Move to the first page, has the same effect as clicking the 'first' button.
480 moveFirst : function(){
481 if (this.fireEvent('beforechange', this, 1) !== false){
482 this.store.loadPage(1);
487 * Move to the previous page, has the same effect as clicking the 'previous' button.
489 movePrevious : function(){
491 prev = me.store.currentPage - 1;
494 if (me.fireEvent('beforechange', me, prev) !== false) {
495 me.store.previousPage();
501 * Move to the next page, has the same effect as clicking the 'next' button.
503 moveNext : function(){
505 total = me.getPageData().pageCount,
506 next = me.store.currentPage + 1;
509 if (me.fireEvent('beforechange', me, next) !== false) {
516 * Move to the last page, has the same effect as clicking the 'last' button.
518 moveLast : function(){
520 last = me.getPageData().pageCount;
522 if (me.fireEvent('beforechange', me, last) !== false) {
523 me.store.loadPage(last);
528 * Refresh the current page, has the same effect as clicking the 'refresh' button.
530 doRefresh : function(){
532 current = me.store.currentPage;
534 if (me.fireEvent('beforechange', me, current) !== false) {
535 me.store.loadPage(current);
540 * Binds the paging toolbar to the specified {@link Ext.data.Store}
541 * @param {Store} store The store to bind to this toolbar
542 * @param {Boolean} initial (Optional) true to not remove listeners
544 bindStore : function(store, initial){
547 if (!initial && me.store) {
548 if(store !== me.store && me.store.autoDestroy){
551 me.store.un('beforeload', me.beforeLoad, me);
552 me.store.un('load', me.onLoad, me);
553 me.store.un('exception', me.onLoadError, me);
560 store = Ext.data.StoreManager.lookup(store);
563 beforeload: me.beforeLoad,
565 exception: me.onLoadError
572 * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
573 * @param {Ext.data.Store} store The data store to unbind
575 unbind : function(store){
576 this.bindStore(null);
580 * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
581 * @param {Ext.data.Store} store The data store to bind
583 bind : function(store){
584 this.bindStore(store);
588 onDestroy : function(){
589 this.bindStore(null);