/** * A {@link Ext.ux.statusbar.StatusBar} plugin that provides automatic error * notification when the associated form contains validation errors. */Ext.define('Ext.ux.statusbar.ValidationStatus', { extend: 'Ext.Component', alias: 'plugin.validationstatus', requires: ['Ext.util.MixedCollection'], /** * @cfg {String} errorIconCls * The {@link Ext.ux.statusbar.StatusBar#iconCls iconCls} value to be applied * to the status message when there is a validation error. */ errorIconCls: 'x-status-error', /** * @cfg {String} errorListCls * The css class to be used for the error list when there are validation errors. */ errorListCls: 'x-status-error-list', /** * @cfg {String} validIconCls * The {@link Ext.ux.statusbar.StatusBar#iconCls iconCls} value to be applied * to the status message when the form validates. */ validIconCls: 'x-status-valid', /** * @cfg {String} showText * The {@link Ext.ux.statusbar.StatusBar#text text} value to be applied when * there is a form validation error. */ showText: 'The form has errors (click for details...)', /** * @cfg {String} hideText * The {@link Ext.ux.statusbar.StatusBar#text text} value to display when * the error list is displayed. */ hideText: 'Click again to hide the error list', /** * @cfg {String} submitText * The {@link Ext.ux.statusbar.StatusBar#text text} value to be applied when * the form is being submitted. */ submitText: 'Saving...', /** * @private */ init: function(sb) { var me = this; me.statusBar = sb; sb.on({ single: true, scope: me, render: me.onStatusbarRender }); sb.on({ click: { element: 'el', fn: me.onStatusClick, scope: me, buffer: 200 } }); }, onStatusbarRender: function(sb) { var me = this, startMonitor = function() { me.monitor = true; }; me.monitor = true; me.errors = Ext.create('Ext.util.MixedCollection'); me.listAlign = (sb.statusAlign === 'right' ? 'br-tr?' : 'bl-tl?'); if (me.form) { // Allow either an id, or a reference to be specified as the form name. me.formPanel = Ext.getCmp(me.form) || me.statusBar.lookupController().lookupReference(me.form); me.basicForm = me.formPanel.getForm(); me.startMonitoring(); me.basicForm.on({ beforeaction: function(f, action) { if (action.type === 'submit') { // Ignore monitoring while submitting otherwise the field validation // events cause the status message to reset too early me.monitor = false; } } }); me.formPanel.on({ beforedestroy: me.destroy, scope: me }); me.basicForm.on('actioncomplete', startMonitor); me.basicForm.on('actionfailed', startMonitor); } }, /** * @private */ startMonitoring: function() { this.basicForm.getFields().each(function(f) { f.on('validitychange', this.onFieldValidation, this); }, this); }, /** * @private */ stopMonitoring: function() { var form = this.basicForm; if (!form.destroyed) { form.getFields().each(function(f) { f.un('validitychange', this.onFieldValidation, this); }, this); } }, doDestroy: function() { Ext.destroy(this.msgEl); this.stopMonitoring(); this.statusBar.statusEl.un('click', this.onStatusClick, this); this.callParent(); }, /** * @private */ onFieldValidation: function(f, isValid) { var me = this, msg; if (!me.monitor) { return false; } msg = f.getErrors()[0]; if (msg) { me.errors.add(f.id, { field: f, msg: msg }); } else { me.errors.removeAtKey(f.id); } this.updateErrorList(); if (me.errors.getCount() > 0) { if (me.statusBar.getText() !== me.showText) { me.statusBar.setStatus({ text: me.showText, iconCls: me.errorIconCls }); } } else { me.statusBar.clearStatus().setIcon(me.validIconCls); } }, /** * @private */ updateErrorList: function() { var me = this, msg, msgEl = me.getMsgEl(); if (me.errors.getCount() > 0) { msg = ['<ul>']; this.errors.each(function(err) { msg.push('<li id="x-err-', err.field.id, '"><a href="#">', err.msg, '</a></li>'); }); msg.push('</ul>'); msgEl.update(msg.join('')); } else { msgEl.update(''); } // reset msgEl size msgEl.setSize('auto', 'auto'); }, /** * @private */ getMsgEl: function() { var me = this, msgEl = me.msgEl, t; if (!msgEl) { msgEl = me.msgEl = Ext.DomHelper.append(Ext.getBody(), { cls: me.errorListCls }, true); msgEl.hide(); msgEl.on('click', function(e) { t = e.getTarget('li', 10, true); if (t) { Ext.getCmp(t.id.split('x-err-')[1]).focus(); me.hideErrors(); } }, null, { stopEvent: true }); // prevent anchor click navigation } return msgEl; }, /** * @private */ showErrors: function() { var me = this; me.updateErrorList(); me.getMsgEl().alignTo(me.statusBar.getEl(), me.listAlign).slideIn( 'b', { duration: 300, easing: 'easeOut' } ); me.statusBar.setText(me.hideText); // hide if the user clicks directly into the form me.formPanel.body.on('click', me.hideErrors, me, { single: true }); }, /** * @private */ hideErrors: function() { var el = this.getMsgEl(); if (el.isVisible()) { el.slideOut('b', { duration: 300, easing: 'easeIn' }); this.statusBar.setText(this.showText); } this.formPanel.body.un('click', this.hideErrors, this); }, /** * @private */ onStatusClick: function() { if (this.getMsgEl().isVisible()) { this.hideErrors(); } else if (this.errors.getCount() > 0) { this.showErrors(); } }});