Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / examples / ux / statusbar / ValidationStatus.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.ux.statusbar.ValidationStatus
17  * A {@link Ext.StatusBar} plugin that provides automatic error notification when the
18  * associated form contains validation errors.
19  * @extends Ext.Component
20  * @constructor
21  * Creates a new ValiationStatus plugin
22  * @param {Object} config A config object
23  */
24 Ext.define('Ext.ux.statusbar.ValidationStatus', {
25     extend: 'Ext.Component', 
26     requires: ['Ext.util.MixedCollection'],
27     /**
28      * @cfg {String} errorIconCls
29      * The {@link #iconCls} value to be applied to the status message when there is a
30      * validation error. Defaults to <tt>'x-status-error'</tt>.
31      */
32     errorIconCls : 'x-status-error',
33     /**
34      * @cfg {String} errorListCls
35      * The css class to be used for the error list when there are validation errors.
36      * Defaults to <tt>'x-status-error-list'</tt>.
37      */
38     errorListCls : 'x-status-error-list',
39     /**
40      * @cfg {String} validIconCls
41      * The {@link #iconCls} value to be applied to the status message when the form
42      * validates. Defaults to <tt>'x-status-valid'</tt>.
43      */
44     validIconCls : 'x-status-valid',
45     
46     /**
47      * @cfg {String} showText
48      * The {@link #text} value to be applied when there is a form validation error.
49      * Defaults to <tt>'The form has errors (click for details...)'</tt>.
50      */
51     showText : 'The form has errors (click for details...)',
52     /**
53      * @cfg {String} showText
54      * The {@link #text} value to display when the error list is displayed.
55      * Defaults to <tt>'Click again to hide the error list'</tt>.
56      */
57     hideText : 'Click again to hide the error list',
58     /**
59      * @cfg {String} submitText
60      * The {@link #text} value to be applied when the form is being submitted.
61      * Defaults to <tt>'Saving...'</tt>.
62      */
63     submitText : 'Saving...',
64     
65     // private
66     init : function(sb){
67         sb.on('render', function(){
68             this.statusBar = sb;
69             this.monitor = true;
70             this.errors = Ext.create('Ext.util.MixedCollection');
71             this.listAlign = (sb.statusAlign === 'right' ? 'br-tr?' : 'bl-tl?');
72             
73             if (this.form) {
74                 this.formPanel = Ext.getCmp(this.form);
75                 this.basicForm = this.formPanel.getForm();
76                 this.startMonitoring();
77                 this.basicForm.on('beforeaction', function(f, action){
78                     if(action.type === 'submit'){
79                         // Ignore monitoring while submitting otherwise the field validation
80                         // events cause the status message to reset too early
81                         this.monitor = false;
82                     }
83                 }, this);
84                 var startMonitor = function(){
85                     this.monitor = true;
86                 };
87                 this.basicForm.on('actioncomplete', startMonitor, this);
88                 this.basicForm.on('actionfailed', startMonitor, this);
89             }
90         }, this, {single:true});
91         sb.on({
92             scope: this,
93             afterlayout:{
94                 single: true,
95                 fn: function(){
96                     // Grab the statusEl after the first layout.
97                     sb.statusEl.getEl().on('click', this.onStatusClick, this, {buffer:200});
98                 } 
99             }, 
100             beforedestroy:{
101                 single: true,
102                 fn: this.onDestroy
103             } 
104         });
105     },
106     
107     // private
108     startMonitoring : function() {
109         this.basicForm.getFields().each(function(f){
110             f.on('validitychange', this.onFieldValidation, this);
111         }, this);
112     },
113     
114     // private
115     stopMonitoring : function(){
116         this.basicForm.getFields().each(function(f){
117             f.un('validitychange', this.onFieldValidation, this);
118         }, this);
119     },
120     
121     // private
122     onDestroy : function(){
123         this.stopMonitoring();
124         this.statusBar.statusEl.un('click', this.onStatusClick, this);
125         this.callParent(arguments);
126     },
127     
128     // private
129     onFieldValidation : function(f, isValid){
130         if (!this.monitor) {
131             return false;
132         }
133         var msg = f.getErrors()[0];
134         if (msg) {
135             this.errors.add(f.id, {field:f, msg:msg});
136         } else {
137             this.errors.removeAtKey(f.id);
138         }
139         this.updateErrorList();
140         if(this.errors.getCount() > 0) {
141             if(this.statusBar.getText() !== this.showText){
142                 this.statusBar.setStatus({text:this.showText, iconCls:this.errorIconCls});
143             }
144         }else{
145             this.statusBar.clearStatus().setIcon(this.validIconCls);
146         }
147     },
148     
149     // private
150     updateErrorList : function(){
151         if(this.errors.getCount() > 0){
152          var msg = '<ul>';
153          this.errors.each(function(err){
154              msg += ('<li id="x-err-'+ err.field.id +'"><a href="#">' + err.msg + '</a></li>');
155          }, this);
156          this.getMsgEl().update(msg+'</ul>');
157         }else{
158             this.getMsgEl().update('');
159         }
160         // reset msgEl size
161         this.getMsgEl().setSize('auto', 'auto');
162     },
163     
164     // private
165     getMsgEl : function(){
166         if(!this.msgEl){
167             this.msgEl = Ext.DomHelper.append(Ext.getBody(), {
168                 cls: this.errorListCls
169             }, true);
170             this.msgEl.hide();
171             this.msgEl.on('click', function(e){
172                 var t = e.getTarget('li', 10, true);
173                 if(t){
174                     Ext.getCmp(t.id.split('x-err-')[1]).focus();
175                     this.hideErrors();
176                 }
177             }, this, {stopEvent:true}); // prevent anchor click navigation
178         }
179         return this.msgEl;
180     },
181     
182     // private
183     showErrors : function(){
184         this.updateErrorList();
185         this.getMsgEl().alignTo(this.statusBar.getEl(), this.listAlign).slideIn('b', {duration: 300, easing:'easeOut'});
186         this.statusBar.setText(this.hideText);
187         this.formPanel.el.on('click', this.hideErrors, this, {single:true}); // hide if the user clicks directly into the form
188     },
189     
190     // private
191     hideErrors : function(){
192         var el = this.getMsgEl();
193         if(el.isVisible()){
194          el.slideOut('b', {duration: 300, easing:'easeIn'});
195          this.statusBar.setText(this.showText);
196         }
197         this.formPanel.el.un('click', this.hideErrors, this);
198     },
199     
200     // private
201     onStatusClick : function(){
202         if(this.getMsgEl().isVisible()){
203             this.hideErrors();
204         }else if(this.errors.getCount() > 0){
205             this.showErrors();
206         }
207     }
208 });