toolbar

Ext JS 4: Notification Bar

I have a view in my Ext JS 4 app that has a grid panel which fills the viewport. I wanted some notification UI for success/failure upon save or other actions but not a modal dialog and not the notification window user extension (which I think looks great). Instead, I wanted a “bar” that would go across the page at the top. I’ve seen it all over the place recently. The way I ended up implementing it was to use a toolbar. This is what it looks like:

The GridBar code is pretty straightforward. I have a small animation to fade the bar in, but haven’t done anything to fade it out.

Ext.define('MyStuff.GridBar', {
	extend: 'Ext.toolbar.Toolbar',
	alias: 'widget.gridbar',
	requires: [
		'Ext.toolbar.*'
	],

	cls: 'gridbar',

	initComponent : function() {
		this.items = [
			{
				itemId: 'msg',
				xtype: 'tbtext',
				text: ''
			},
			'->',
			{
				text: 'X',
				scope: this,
				handler: this.hideBar
			}
		]

		this.callParent(arguments);
		this.msgItem = this.child('#msg');
	},

	showSuccess : function(msg) {
		this.getEl().setOpacity(0.25, false);
		this.addClass('gridbar-success');
		this.showBar(msg);
	},

	showError : function(msg) {
		this.getEl().setOpacity(0.25, false);
		this.addClass('gridbar-error');
		this.showBar(msg);
	},

	showBar : function(msg) {
		this.msgItem.update(msg);
		this.show();
		this.getEl().fadeIn({opacity: 1, duration: 1000});
		this.ownerCt.forceComponentLayout();
	},

	hideBar : function () {
		if (!this.isHidden()) {
			this.hide();
			this.ownerCt.forceComponentLayout();
			this.removeCls("gridbar-success");
			this.removeCls("gridbar-error");
			this.msgItem.update('');
		}
	},

    // Add custom processing to the onRender phase.
    afterRender: function() {
        this.callParent(arguments);
		this.hide();
    }
});

I used the following css:

.gridbar {
    margin: 3px !important;
}
.gridbar-success, .x-nlg .gridbar-success {
    border: 1px solid #AEE0AE !important;
    background-color: #D1EED1 !important;
    background-image: none !important;
}
.gridbar-error, .x-nlg .gridbar-error {
    border:  1px solid #F79897 !important;
    background-color: #FDDFDE !important;
    background-image: none !important;
}

The way to use it is to just add the GridBar into the docked items. Like this:

 {
			xtype: 'grid',
			itemId: 'myGrid',
			// more grid configs
			columns : [
				// column definitions here
			],
			dockedItems : [
				{
					itemId: 'gridBar',
					xtype: 'gridbar',
					dock: 'top'
				},
				{
					xtype: 'toolbar',
					dock: 'top',
					items: [
						// put in whatever items you have here
					]
				}
			]
		};

By default, the GridBar will be hidden on render. I had to call forceComponentLayout whenever hiding and showing the GridBar to get the UI to look correct. I tried many things to not call it, but that was the only thing that seemed to work.

Ext 4.1.0 Update
I revisited my “notification bar” with the release of Ext JS 4.1.0, and I was able to streamline it a little bit. Most importantly, I was able to remove the forceComponentLayout. Here is this code. I updated the name to be a little more generic, from GridBar to NotifyBar. Usage is the same as above.

ExtJS Paging Toolbar that can right justify

I wanted a paging toolbar that could be placed in right-side of the toolbar, allowing you to put your own buttons on the left-side. Since PagingToolbar.initComponent decides where the fill goes, I had to extend and override initComponent so that I had control over the fill. I also added my own param alignPagingRight that defaults to false. When it is set to true, it will put the paging control on the right side of the fill.


Ext.namespace("Ext.ux");
var T = Ext.Toolbar;
/**
 * @class Ext.ux.JustifyPagingToolbar
 * @extends Ext.PagingToolbar
 * <p>This just extends the standard ExtJS paging toolbar so that the paging 
 * parts of the toolbar can be aligned on the right side of the toolbar. The
 * code in initComponent is copied directly from Ext.PagingToolbar, but with 
 * some slight modifications and the addition of the alignPagingRight config
 * item</p>
 * @constructor
 * Create a new Ext.ux.JustifyPagingToolbar
 * @param {Object} config The config object
 * @xtype justifypagingtoolbar
 */
Ext.ux.JustifyPagingToolbar= Ext.extend(Ext.PagingToolbar, {

    /**
     * @cfg {Boolean} alignPagingRight
     * <tt>true</tt> to put the paging toolbar items after a fill item, and
	 * therefore on the right-side of the toolbar.  Will put the displayInfo
	 * text immediately after the paging toolbar items.  If this is false, the
	 * paging toolbar items and and user items will appear on the left-side.
     * Defaults to <tt>false</tt>.
     */
	alignPagingRight : false,
	
	initComponent : function() {
       var pagingItems = [this.first = new T.Button({
            tooltip: this.firstText,
            overflowText: this.firstText,
            iconCls: 'x-tbar-page-first',
            disabled: true,
            handler: this.moveFirst,
            scope: this
        }), this.prev = new T.Button({
            tooltip: this.prevText,
            overflowText: this.prevText,
            iconCls: 'x-tbar-page-prev',
            disabled: true,
            handler: this.movePrevious,
            scope: this
        }), '-', this.beforePageText,
        this.inputItem = new Ext.form.NumberField({
            cls: 'x-tbar-page-number',
            allowDecimals: false,
            allowNegative: false,
            enableKeyEvents: true,
            selectOnFocus: true,
            listeners: {
                scope: this,
                keydown: this.onPagingKeyDown,
                blur: this.onPagingBlur
            }
        }), this.afterTextItem = new T.TextItem({
            text: String.format(this.afterPageText, 1)
        }), '-', this.next = new T.Button({
            tooltip: this.nextText,
            overflowText: this.nextText,
            iconCls: 'x-tbar-page-next',
            disabled: true,
            handler: this.moveNext,
            scope: this
        }), this.last = new T.Button({
            tooltip: this.lastText,
            overflowText: this.lastText,
            iconCls: 'x-tbar-page-last',
            disabled: true,
            handler: this.moveLast,
            scope: this
        }), '-', this.refresh = new T.Button({
            tooltip: this.refreshText,
            overflowText: this.refreshText,
            iconCls: 'x-tbar-loading',
            handler: this.refresh,
            scope: this
        })];


        var userItems = this.items || this.buttons || [];
		
		/* override here */
		if (this.alignPagingRight) {
			userItems.push('->');
		}
		/* end override */
		
        if (this.prependButtons) {
            this.items = userItems.concat(pagingItems);
        }else{
            this.items = pagingItems.concat(userItems);
        }
        delete this.buttons;
        if(this.displayInfo){
			/* override here */
			if ( !this.alignPagingRight) {
				this.items.push('->');
			}
			/* end override */
            this.items.push(this.displayItem = new T.TextItem({}));
        }
        Ext.PagingToolbar.superclass.initComponent.call(this);
        this.addEvents(
            /**
             * @event change
             * Fires after the active page has been changed.
             * @param {Ext.PagingToolbar} this
             * @param {Object} pageData An object that has these properties:<ul>
             * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
             * returned by the server</div></li>
             * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
             * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
             * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
             * </ul>
             */
            'change',
            /**
             * @event beforechange
             * Fires just before the active page is changed.
             * Return false to prevent the active page from being changed.
             * @param {Ext.PagingToolbar} this
             * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
             * loading the required page. This will contain:<ul>
             * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
             * be retrieved from the server</div></li>
             * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
             * </ul>
             * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
             * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
             * <p>Parameters may be added as required in the event handler.</p>
             */
            'beforechange'
        );
        this.on('afterlayout', this.onFirstLayout, this, {single: true});
        this.cursor = 0;
        this.bindStore(this.store);
	}

});

Ext.reg('justifypagingtoolbar', Ext.ux.JustifyPagingToolbar);