9 * @class Roo.bootstrap.Form
10 * @extends Roo.bootstrap.Component
11 * Bootstrap Form class
12 * @cfg {String} method GET | POST (default POST)
13 * @cfg {String} labelAlign top | left (default top)
14 * @cfg {String} align left | right - for navbars
19 * @param {Object} config The config object
23 Roo.bootstrap.Form = function(config){
24 Roo.bootstrap.Form.superclass.constructor.call(this, config);
27 * @event clientvalidation
28 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30 * @param {Boolean} valid true if the form has passed client-side validation
32 clientvalidation: true,
35 * Fires before any action is performed. Return false to cancel the action.
37 * @param {Action} action The action to be performed
42 * Fires when an action fails.
44 * @param {Action} action The action that failed
48 * @event actioncomplete
49 * Fires when an action is completed.
51 * @param {Action} action The action that completed
58 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
61 * @cfg {String} method
62 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
67 * The URL to use for form actions if one isn't supplied in the action options.
70 * @cfg {Boolean} fileUpload
71 * Set to true if this form is a file upload.
75 * @cfg {Object} baseParams
76 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
80 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
84 * @cfg {Sting} align (left|right) for navbar forms
92 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
93 * element by passing it or its id or mask the form itself by passing in true.
96 waitMsgTarget : false,
101 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
102 * element by passing it or its id or mask the form itself by passing in true.
106 getAutoCreate : function(){
110 method : this.method || 'POST',
111 id : this.id || Roo.id(),
114 if (this.parent().xtype.match(/^Nav/)) {
115 cfg.cls = 'navbar-form navbar-' + this.align;
119 if (this.labelAlign == 'left' ) {
120 cfg.cls += ' form-horizontal';
126 initEvents : function()
128 this.el.on('submit', this.onSubmit, this);
133 onSubmit : function(e){
138 * Returns true if client-side validation on the form is successful.
141 isValid : function(){
142 var items = this.getItems();
144 items.each(function(f){
153 * Returns true if any fields in this form have changed since their original load.
156 isDirty : function(){
158 var items = this.getItems();
159 items.each(function(f){
169 * Performs a predefined action (submit or load) or custom actions you define on this form.
170 * @param {String} actionName The name of the action type
171 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
172 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
173 * accept other config options):
175 Property Type Description
176 ---------------- --------------- ----------------------------------------------------------------------------------
177 url String The url for the action (defaults to the form's url)
178 method String The form method to use (defaults to the form's method, or POST if not defined)
179 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
180 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
181 validate the form on the client (defaults to false)
183 * @return {BasicForm} this
185 doAction : function(action, options){
186 if(typeof action == 'string'){
187 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
190 if(this.fireEvent('beforeaction', this, action) !== false){
192 this.beforeAction(action);
193 action.run.defer(100, action);
199 beforeAction : function(action){
200 var o = action.options;
202 // not really supported yet.. ??
204 //if(this.waitMsgTarget === true){
205 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
206 //}else if(this.waitMsgTarget){
207 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
208 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
210 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
216 afterAction : function(action, success){
217 this.activeAction = null;
218 var o = action.options;
220 //if(this.waitMsgTarget === true){
222 //}else if(this.waitMsgTarget){
223 // this.waitMsgTarget.unmask();
225 // Roo.MessageBox.updateProgress(1);
226 // Roo.MessageBox.hide();
233 Roo.callback(o.success, o.scope, [this, action]);
234 this.fireEvent('actioncomplete', this, action);
237 Roo.log('go into the fail??');
238 // failure condition..
239 // we have a scenario where updates need confirming.
240 // eg. if a locking scenario exists..
241 // we look for { errors : { needs_confirm : true }} in the response.
243 (typeof(action.result) != 'undefined') &&
244 (typeof(action.result.errors) != 'undefined') &&
245 (typeof(action.result.errors.needs_confirm) != 'undefined')
248 Roo.log("not supported yet");
251 Roo.MessageBox.confirm(
252 "Change requires confirmation",
253 action.result.errorMsg,
258 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
269 Roo.callback(o.failure, o.scope, [this, action]);
270 // show an error message if no failed handler is set..
271 if (!this.hasListener('actionfailed')) {
272 Roo.log("need to add dialog support");
274 Roo.MessageBox.alert("Error",
275 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
276 action.result.errorMsg :
277 "Saving Failed, please check your entries or try again"
281 Roo.log('got the end??!!');
282 this.fireEvent('actionfailed', this, action);
287 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
288 * @param {String} id The value to search for
291 findField : function(id){
292 var items = this.getItems();
293 var field = items.get(id);
295 items.each(function(f){
296 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
303 return field || null;
306 * Mark fields in this form invalid in bulk.
307 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
308 * @return {BasicForm} this
310 markInvalid : function(errors){
311 if(errors instanceof Array){
312 for(var i = 0, len = errors.length; i < len; i++){
313 var fieldError = errors[i];
314 var f = this.findField(fieldError.id);
316 f.markInvalid(fieldError.msg);
322 if(typeof errors[id] != 'function' && (field = this.findField(id))){
323 field.markInvalid(errors[id]);
327 //Roo.each(this.childForms || [], function (f) {
328 // f.markInvalid(errors);
335 * Set values for fields in this form in bulk.
336 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
337 * @return {BasicForm} this
339 setValues : function(values){
340 if(values instanceof Array){ // array of objects
341 for(var i = 0, len = values.length; i < len; i++){
343 var f = this.findField(v.id);
346 if(this.trackResetOnLoad){
347 f.originalValue = f.getValue();
351 }else{ // object hash
354 if(typeof values[id] != 'function' && (field = this.findField(id))){
356 if (field.setFromData &&
358 field.displayField &&
359 // combos' with local stores can
360 // be queried via setValue()
361 // to set their value..
362 (field.store && !field.store.isLocal)
366 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
367 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
368 field.setFromData(sd);
371 field.setValue(values[id]);
375 if(this.trackResetOnLoad){
376 field.originalValue = field.getValue();
382 //Roo.each(this.childForms || [], function (f) {
383 // f.setValues(values);
390 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
391 * they are returned as an array.
392 * @param {Boolean} asString
395 getValues : function(asString){
396 //if (this.childForms) {
397 // copy values from the child forms
398 // Roo.each(this.childForms, function (f) {
399 // this.setValues(f.getValues());
405 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
406 if(asString === true){
409 return Roo.urlDecode(fs);
413 * Returns the fields in this form as an object with key/value pairs.
414 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
417 getFieldValues : function(with_hidden)
419 var items = this.getItems();
421 items.each(function(f){
425 var v = f.getValue();
426 if (f.inputType =='radio') {
427 if (typeof(ret[f.getName()]) == 'undefined') {
428 ret[f.getName()] = ''; // empty..
431 if (!f.el.dom.checked) {
439 // not sure if this supported any more..
440 if ((typeof(v) == 'object') && f.getRawValue) {
441 v = f.getRawValue() ; // dates..
443 // combo boxes where name != hiddenName...
444 if (f.name != f.getName()) {
445 ret[f.name] = f.getRawValue();
447 ret[f.getName()] = v;
454 * Clears all invalid messages in this form.
455 * @return {BasicForm} this
457 clearInvalid : function(){
458 var items = this.getItems();
460 items.each(function(f){
471 * @return {BasicForm} this
474 var items = this.getItems();
475 items.each(function(f){
479 Roo.each(this.childForms || [], function (f) {
486 getItems : function()
488 var r=new Roo.util.MixedCollection(false, function(o){
489 return o.id || (o.id = Roo.id());
491 var iter = function(el) {
498 Roo.each(el.items,function(e) {