4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
13 * @class Roo.form.BasicForm
14 * @extends Roo.util.Observable
15 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
17 * @param {String/HTMLElement/Roo.Element} el The form element or its id
18 * @param {Object} config Configuration options
20 Roo.form.BasicForm = function(el, config){
23 Roo.apply(this, config);
25 * The Roo.form.Field items in this form.
26 * @type MixedCollection
30 this.items = new Roo.util.MixedCollection(false, function(o){
31 return o.id || (o.id = Roo.id());
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.form.BasicForm.superclass.constructor.call(this);
62 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
64 * @cfg {String} method
65 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
68 * @cfg {DataReader} reader
69 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
70 * This is optional as there is built-in support for processing JSON.
73 * @cfg {DataReader} errorReader
74 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
75 * This is completely optional as there is built-in support for processing JSON.
79 * The URL to use for form actions if one isn't supplied in the action options.
82 * @cfg {Boolean} fileUpload
83 * Set to true if this form is a file upload.
87 * @cfg {Object} baseParams
88 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
93 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
101 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
102 * or setValues() data instead of when the form was first created.
104 trackResetOnLoad : false,
108 * childForms - used for multi-tab forms
114 * allItems - full list of fields.
120 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
121 * element by passing it or its id or mask the form itself by passing in true.
124 waitMsgTarget : false,
127 initEl : function(el){
128 this.el = Roo.get(el);
129 this.id = this.el.id || Roo.id();
130 this.el.on('submit', this.onSubmit, this);
131 this.el.addClass('x-form');
135 onSubmit : function(e){
140 * Returns true if client-side validation on the form is successful.
143 isValid : function(){
145 this.items.each(function(f){
154 * Returns true if any fields in this form have changed since their original load.
157 isDirty : function(){
159 this.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);
189 if(this.fireEvent('beforeaction', this, action) !== false){
190 this.beforeAction(action);
191 action.run.defer(100, action);
197 * Shortcut to do a submit action.
198 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
199 * @return {BasicForm} this
201 submit : function(options){
202 this.doAction('submit', options);
207 * Shortcut to do a load action.
208 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
209 * @return {BasicForm} this
211 load : function(options){
212 this.doAction('load', options);
217 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
218 * @param {Record} record The record to edit
219 * @return {BasicForm} this
221 updateRecord : function(record){
223 var fs = record.fields;
225 var field = this.findField(f.name);
227 record.set(f.name, field.getValue());
235 * Loads an Roo.data.Record into this form.
236 * @param {Record} record The record to load
237 * @return {BasicForm} this
239 loadRecord : function(record){
240 this.setValues(record.data);
245 beforeAction : function(action){
246 var o = action.options;
249 if(this.waitMsgTarget === true){
250 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
251 }else if(this.waitMsgTarget){
252 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
253 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
255 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
261 afterAction : function(action, success){
262 this.activeAction = null;
263 var o = action.options;
265 if(this.waitMsgTarget === true){
267 }else if(this.waitMsgTarget){
268 this.waitMsgTarget.unmask();
270 Roo.MessageBox.updateProgress(1);
271 Roo.MessageBox.hide();
278 Roo.callback(o.success, o.scope, [this, action]);
279 this.fireEvent('actioncomplete', this, action);
283 // failure condition..
284 // we have a scenario where updates need confirming.
285 // eg. if a locking scenario exists..
286 // we look for { errors : { needs_confirm : true }} in the response.
288 (typeof(action.result) != 'undefined') &&
289 (typeof(action.result.errors) != 'undefined') &&
290 (typeof(action.result.errors.needs_confirm) != 'undefined')
293 Roo.MessageBox.confirm(
294 "Change requires confirmation",
295 action.result.errorMsg,
300 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
310 Roo.callback(o.failure, o.scope, [this, action]);
311 // show an error message if no failed handler is set..
312 if (!this.hasListener('actionfailed')) {
313 Roo.MessageBox.alert("Error",
314 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
315 action.result.errorMsg :
316 "Saving Failed, please check your entries or try again"
320 this.fireEvent('actionfailed', this, action);
326 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
327 * @param {String} id The value to search for
330 findField : function(id){
331 var field = this.items.get(id);
333 this.items.each(function(f){
334 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
340 return field || null;
344 * Add a secondary form to this one,
345 * Used to provide tabbed forms. One form is primary, with hidden values
346 * which mirror the elements from the other forms.
348 * @param {Roo.form.Form} form to add.
351 addForm : function(form)
354 if (this.childForms.indexOf(form) > -1) {
358 this.childForms.push(form);
360 Roo.each(form.allItems, function (fe) {
362 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
363 if (this.findField(n)) { // already added..
366 var add = new Roo.form.Hidden({
376 * Mark fields in this form invalid in bulk.
377 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
378 * @return {BasicForm} this
380 markInvalid : function(errors){
381 if(errors instanceof Array){
382 for(var i = 0, len = errors.length; i < len; i++){
383 var fieldError = errors[i];
384 var f = this.findField(fieldError.id);
386 f.markInvalid(fieldError.msg);
392 if(typeof errors[id] != 'function' && (field = this.findField(id))){
393 field.markInvalid(errors[id]);
397 Roo.each(this.childForms || [], function (f) {
398 f.markInvalid(errors);
405 * Set values for fields in this form in bulk.
406 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
407 * @return {BasicForm} this
409 setValues : function(values){
410 if(values instanceof Array){ // array of objects
411 for(var i = 0, len = values.length; i < len; i++){
413 var f = this.findField(v.id);
416 if(this.trackResetOnLoad){
417 if(typeof(field.getValue()) == 'undefined'){
420 f.originalValue = f.getValue();
424 }else{ // object hash
427 if(typeof values[id] != 'function' && (field = this.findField(id))){
429 if (field.setFromData &&
431 field.displayField &&
432 // combos' with local stores can
433 // be queried via setValue()
434 // to set their value..
435 (field.store && !field.store.isLocal)
439 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
440 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
441 field.setFromData(sd);
444 field.setValue(values[id]);
448 if(this.trackResetOnLoad){
449 if(typeof(field.getValue()) == 'undefined'){
452 field.originalValue = field.getValue();
458 Roo.each(this.childForms || [], function (f) {
466 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
467 * they are returned as an array.
468 * @param {Boolean} asString
471 getValues : function(asString){
472 if (this.childForms) {
473 // copy values from the child forms
474 Roo.each(this.childForms, function (f) {
475 this.setValues(f.getValues());
481 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
482 if(asString === true){
485 return Roo.urlDecode(fs);
489 * Returns the fields in this form as an object with key/value pairs.
490 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
493 getFieldValues : function(with_hidden)
495 if (this.childForms) {
496 // copy values from the child forms
497 // should this call getFieldValues - probably not as we do not currently copy
498 // hidden fields when we generate..
499 Roo.each(this.childForms, function (f) {
500 this.setValues(f.getValues());
505 this.items.each(function(f){
509 var v = f.getValue();
510 if (f.inputType =='radio') {
511 if (typeof(ret[f.getName()]) == 'undefined') {
512 ret[f.getName()] = ''; // empty..
515 if (!f.el.dom.checked) {
523 // not sure if this supported any more..
524 if ((typeof(v) == 'object') && f.getRawValue) {
525 v = f.getRawValue() ; // dates..
527 // combo boxes where name != hiddenName...
528 if (f.name != f.getName()) {
529 ret[f.name] = f.getRawValue();
531 ret[f.getName()] = v;
538 * Clears all invalid messages in this form.
539 * @return {BasicForm} this
541 clearInvalid : function(){
542 this.items.each(function(f){
546 Roo.each(this.childForms || [], function (f) {
556 * @return {BasicForm} this
559 this.items.each(function(f){
563 Roo.each(this.childForms || [], function (f) {
572 * Add Roo.form components to this form.
573 * @param {Field} field1
574 * @param {Field} field2 (optional)
575 * @param {Field} etc (optional)
576 * @return {BasicForm} this
579 this.items.addAll(Array.prototype.slice.call(arguments, 0));
585 * Removes a field from the items collection (does NOT remove its markup).
586 * @param {Field} field
587 * @return {BasicForm} this
589 remove : function(field){
590 this.items.remove(field);
595 * Looks at the fields in this form, checks them for an id attribute,
596 * and calls applyTo on the existing dom element with that id.
597 * @return {BasicForm} this
600 this.items.each(function(f){
601 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
609 * Calls {@link Ext#apply} for all fields in this form with the passed object.
610 * @param {Object} values
611 * @return {BasicForm} this
613 applyToFields : function(o){
614 this.items.each(function(f){
621 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
622 * @param {Object} values
623 * @return {BasicForm} this
625 applyIfToFields : function(o){
626 this.items.each(function(f){
634 Roo.BasicForm = Roo.form.BasicForm;