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
15 * @cfg {Boolean} loadMask load mask when submit (default true)
20 * @param {Object} config The config object
24 Roo.bootstrap.Form = function(config){
25 Roo.bootstrap.Form.superclass.constructor.call(this, config);
28 * @event clientvalidation
29 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
31 * @param {Boolean} valid true if the form has passed client-side validation
33 clientvalidation: true,
36 * Fires before any action is performed. Return false to cancel the action.
38 * @param {Action} action The action to be performed
43 * Fires when an action fails.
45 * @param {Action} action The action that failed
49 * @event actioncomplete
50 * Fires when an action is completed.
52 * @param {Action} action The action that completed
59 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
62 * @cfg {String} method
63 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
68 * The URL to use for form actions if one isn't supplied in the action options.
71 * @cfg {Boolean} fileUpload
72 * Set to true if this form is a file upload.
76 * @cfg {Object} baseParams
77 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
81 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
85 * @cfg {Sting} align (left|right) for navbar forms
93 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
94 * element by passing it or its id or mask the form itself by passing in true.
97 waitMsgTarget : false,
102 * @cfg {Boolean} errPopover (true|false) default false
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);
129 // this was added as random key presses on the form where triggering form submit.
130 this.el.on('keypress', function(e) {
131 if (e.getCharCode() != 13) {
134 // we might need to allow it for textareas.. and some other items.
135 // check e.getTarget().
137 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
141 Roo.log("keypress blocked");
150 onSubmit : function(e){
155 * Returns true if client-side validation on the form is successful.
158 isValid : function(){
159 var items = this.getItems();
163 items.each(function(f){
177 if(this.errPopover && !valid){
178 this.showErrPopover(target);
184 showErrPopover : function(target)
186 if(!this.errPopover){
190 var oIndex = target.el.getStyle('z-index');
192 target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
194 target.el.addClass('roo-invalid-outline');
196 target.inputEl().focus();
198 var tooltip = new Roo.bootstrap.Tooltip();
199 tooltip.bindEl = target.el;
201 var fadeout = function(){
203 target.inputEl().un('blur', fadeout);
204 target.inputEl().un('keyup', fadeout);
206 target.el.setStyle('z-index', oIndex);
208 target.el.removeClass('roo-invalid-outline');
210 if(!intervalFadeOut){
214 window.clearInterval(intervalFadeOut);
215 intervalFadeOut = false;
219 // target.inputEl().on('blur', fadeout, target);
220 // target.inputEl().on('keyup', fadeout, target);
222 // if(intervalFadeOut){
223 // window.clearInterval(intervalFadeOut);
224 // intervalFadeOut = false;
227 // var intervalFadeOut = window.setInterval(function() {
235 * Returns true if any fields in this form have changed since their original load.
238 isDirty : function(){
240 var items = this.getItems();
241 items.each(function(f){
251 * Performs a predefined action (submit or load) or custom actions you define on this form.
252 * @param {String} actionName The name of the action type
253 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
254 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
255 * accept other config options):
257 Property Type Description
258 ---------------- --------------- ----------------------------------------------------------------------------------
259 url String The url for the action (defaults to the form's url)
260 method String The form method to use (defaults to the form's method, or POST if not defined)
261 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
262 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
263 validate the form on the client (defaults to false)
265 * @return {BasicForm} this
267 doAction : function(action, options){
268 if(typeof action == 'string'){
269 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
271 if(this.fireEvent('beforeaction', this, action) !== false){
272 this.beforeAction(action);
273 action.run.defer(100, action);
279 beforeAction : function(action){
280 var o = action.options;
283 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
285 // not really supported yet.. ??
287 //if(this.waitMsgTarget === true){
288 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
289 //}else if(this.waitMsgTarget){
290 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
291 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
293 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
299 afterAction : function(action, success){
300 this.activeAction = null;
301 var o = action.options;
303 //if(this.waitMsgTarget === true){
305 //}else if(this.waitMsgTarget){
306 // this.waitMsgTarget.unmask();
308 // Roo.MessageBox.updateProgress(1);
309 // Roo.MessageBox.hide();
316 Roo.callback(o.success, o.scope, [this, action]);
317 this.fireEvent('actioncomplete', this, action);
321 // failure condition..
322 // we have a scenario where updates need confirming.
323 // eg. if a locking scenario exists..
324 // we look for { errors : { needs_confirm : true }} in the response.
326 (typeof(action.result) != 'undefined') &&
327 (typeof(action.result.errors) != 'undefined') &&
328 (typeof(action.result.errors.needs_confirm) != 'undefined')
331 Roo.log("not supported yet");
334 Roo.MessageBox.confirm(
335 "Change requires confirmation",
336 action.result.errorMsg,
341 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
351 Roo.callback(o.failure, o.scope, [this, action]);
352 // show an error message if no failed handler is set..
353 if (!this.hasListener('actionfailed')) {
354 Roo.log("need to add dialog support");
356 Roo.MessageBox.alert("Error",
357 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
358 action.result.errorMsg :
359 "Saving Failed, please check your entries or try again"
364 this.fireEvent('actionfailed', this, action);
369 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
370 * @param {String} id The value to search for
373 findField : function(id){
374 var items = this.getItems();
375 var field = items.get(id);
377 items.each(function(f){
378 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
385 return field || null;
388 * Mark fields in this form invalid in bulk.
389 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
390 * @return {BasicForm} this
392 markInvalid : function(errors){
393 if(errors instanceof Array){
394 for(var i = 0, len = errors.length; i < len; i++){
395 var fieldError = errors[i];
396 var f = this.findField(fieldError.id);
398 f.markInvalid(fieldError.msg);
404 if(typeof errors[id] != 'function' && (field = this.findField(id))){
405 field.markInvalid(errors[id]);
409 //Roo.each(this.childForms || [], function (f) {
410 // f.markInvalid(errors);
417 * Set values for fields in this form in bulk.
418 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
419 * @return {BasicForm} this
421 setValues : function(values){
422 if(values instanceof Array){ // array of objects
423 for(var i = 0, len = values.length; i < len; i++){
425 var f = this.findField(v.id);
428 if(this.trackResetOnLoad){
429 f.originalValue = f.getValue();
433 }else{ // object hash
436 if(typeof values[id] != 'function' && (field = this.findField(id))){
438 if (field.setFromData &&
440 field.displayField &&
441 // combos' with local stores can
442 // be queried via setValue()
443 // to set their value..
444 (field.store && !field.store.isLocal)
448 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
449 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
450 field.setFromData(sd);
453 field.setValue(values[id]);
457 if(this.trackResetOnLoad){
458 field.originalValue = field.getValue();
464 //Roo.each(this.childForms || [], function (f) {
465 // f.setValues(values);
472 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
473 * they are returned as an array.
474 * @param {Boolean} asString
477 getValues : function(asString){
478 //if (this.childForms) {
479 // copy values from the child forms
480 // Roo.each(this.childForms, function (f) {
481 // this.setValues(f.getValues());
487 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
488 if(asString === true){
491 return Roo.urlDecode(fs);
495 * Returns the fields in this form as an object with key/value pairs.
496 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
499 getFieldValues : function(with_hidden)
501 var items = this.getItems();
503 items.each(function(f){
507 var v = f.getValue();
508 if (f.inputType =='radio') {
509 if (typeof(ret[f.getName()]) == 'undefined') {
510 ret[f.getName()] = ''; // empty..
513 if (!f.el.dom.checked) {
521 // not sure if this supported any more..
522 if ((typeof(v) == 'object') && f.getRawValue) {
523 v = f.getRawValue() ; // dates..
525 // combo boxes where name != hiddenName...
526 if (f.name != f.getName()) {
527 ret[f.name] = f.getRawValue();
529 ret[f.getName()] = v;
536 * Clears all invalid messages in this form.
537 * @return {BasicForm} this
539 clearInvalid : function(){
540 var items = this.getItems();
542 items.each(function(f){
553 * @return {BasicForm} this
556 var items = this.getItems();
557 items.each(function(f){
561 Roo.each(this.childForms || [], function (f) {
568 getItems : function()
570 var r=new Roo.util.MixedCollection(false, function(o){
571 return o.id || (o.id = Roo.id());
573 var iter = function(el) {
580 Roo.each(el.items,function(e) {