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.
86 * @cfg {Object} baseParams
87 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
90 * @cfg {Roo.Element} maskEl
91 * Element to mask when loading or savling
94 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
102 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
103 * or setValues() data instead of when the form was first created.
105 trackResetOnLoad : false,
109 * childForms - used for multi-tab forms
115 * allItems - full list of fields.
121 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
122 * element by passing it or its id or mask the form itself by passing in true.
125 waitMsgTarget : undefined,
128 initEl : function(el){
129 this.el = Roo.get(el);
130 this.id = this.el.id || Roo.id();
131 this.el.on('submit', this.onSubmit, this);
132 this.el.addClass('x-form');
136 onSubmit : function(e){
141 * Returns true if client-side validation on the form is successful.
144 isValid : function(){
146 this.items.each(function(f){
155 * Returns true if any fields in this form have changed since their original load.
158 isDirty : function(){
160 this.items.each(function(f){
170 * Performs a predefined action (submit or load) or custom actions you define on this form.
171 * @param {String} actionName The name of the action type
172 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
173 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
174 * accept other config options):
176 Property Type Description
177 ---------------- --------------- ----------------------------------------------------------------------------------
178 url String The url for the action (defaults to the form's url)
179 method String The form method to use (defaults to the form's method, or POST if not defined)
180 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
181 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
182 validate the form on the client (defaults to false)
184 * @return {BasicForm} this
186 doAction : function(action, options){
187 if(typeof action == 'string'){
188 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
190 if(this.fireEvent('beforeaction', this, action) !== false){
191 this.beforeAction(action);
192 action.run.defer(100, action);
198 * Shortcut to do a submit action.
199 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
200 * @return {BasicForm} this
202 submit : function(options){
203 this.doAction('submit', options);
208 * Shortcut to do a load action.
209 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
210 * @return {BasicForm} this
212 load : function(options){
213 this.doAction('load', options);
218 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
219 * @param {Record} record The record to edit
220 * @return {BasicForm} this
222 updateRecord : function(record){
224 var fs = record.fields;
226 var field = this.findField(f.name);
228 record.set(f.name, field.getValue());
236 * Loads an Roo.data.Record into this form.
237 * @param {Record} record The record to load
238 * @return {BasicForm} this
240 loadRecord : function(record){
241 this.setValues(record.data);
246 beforeAction : function(action){
247 var o = action.options;
249 if(this.waitMsgTarget === true){
250 this.el.mask(o.waitMsg, 'x-mask-loading');
251 }else if(this.waitMsgTarget){
252 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
253 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
255 Roo.MessageBox.wait(o.waitMsg, 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);
282 Roo.callback(o.failure, o.scope, [this, action]);
284 if (!this.hasListener('actionfailed')) {
286 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
289 this.fireEvent('actionfailed', this, action);
292 this.maskEl.unmask();
297 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
298 * @param {String} id The value to search for
301 findField : function(id){
302 var field = this.items.get(id);
304 this.items.each(function(f){
305 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
311 return field || null;
315 * Add a secondary form to this one,
316 * Used to provide tabbed forms. One form is primary, with hidden values
317 * which mirror the elements from the other forms.
319 * @param {Roo.form.Form} form to add.
322 addForm : function(form)
325 if (this.childForms.indexOf(form) > -1) {
329 this.childForms.push(form);
331 Roo.each(form.allItems, function (fe) {
333 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
334 if (this.findField(n)) { // already added..
337 var add = new Roo.form.Hidden({
347 * Mark fields in this form invalid in bulk.
348 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
349 * @return {BasicForm} this
351 markInvalid : function(errors){
352 if(errors instanceof Array){
353 for(var i = 0, len = errors.length; i < len; i++){
354 var fieldError = errors[i];
355 var f = this.findField(fieldError.id);
357 f.markInvalid(fieldError.msg);
363 if(typeof errors[id] != 'function' && (field = this.findField(id))){
364 field.markInvalid(errors[id]);
368 Roo.each(this.childForms || [], function (f) {
369 f.markInvalid(errors);
376 * Set values for fields in this form in bulk.
377 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
378 * @return {BasicForm} this
380 setValues : function(values){
381 if(values instanceof Array){ // array of objects
382 for(var i = 0, len = values.length; i < len; i++){
384 var f = this.findField(v.id);
387 if(this.trackResetOnLoad){
388 f.originalValue = f.getValue();
392 }else{ // object hash
395 if(typeof values[id] != 'function' && (field = this.findField(id))){
397 if (field.setFromData &&
399 field.displayField &&
400 // combos' with local stores can
401 // be queried via setValue()
402 // to set their value..
403 (field.store && !field.store.isLocal)
407 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
408 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
409 field.setFromData(sd);
412 field.setValue(values[id]);
416 if(this.trackResetOnLoad){
417 field.originalValue = field.getValue();
423 Roo.each(this.childForms || [], function (f) {
431 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
432 * they are returned as an array.
433 * @param {Boolean} asString
436 getValues : function(asString){
437 if (this.childForms) {
438 // copy values from the child forms
439 Roo.each(this.childForms, function (f) {
440 this.setValues(f.getValues());
446 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
447 if(asString === true){
450 return Roo.urlDecode(fs);
454 * Returns the fields in this form as an object with key/value pairs.
455 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
458 getFieldValues : function()
460 if (this.childForms) {
461 // copy values from the child forms
462 Roo.each(this.childForms, function (f) {
463 this.setValues(f.getValues());
468 this.items.each(function(f){
472 var v = f.getValue();
473 if ((typeof(v) == 'object') && f.getRawValue) {
474 v = f.getRawValue() ; // dates..
476 ret[f.getName()] = v;
483 * Clears all invalid messages in this form.
484 * @return {BasicForm} this
486 clearInvalid : function(){
487 this.items.each(function(f){
491 Roo.each(this.childForms || [], function (f) {
501 * @return {BasicForm} this
504 this.items.each(function(f){
508 Roo.each(this.childForms || [], function (f) {
517 * Add Roo.form components to this form.
518 * @param {Field} field1
519 * @param {Field} field2 (optional)
520 * @param {Field} etc (optional)
521 * @return {BasicForm} this
524 this.items.addAll(Array.prototype.slice.call(arguments, 0));
530 * Removes a field from the items collection (does NOT remove its markup).
531 * @param {Field} field
532 * @return {BasicForm} this
534 remove : function(field){
535 this.items.remove(field);
540 * Looks at the fields in this form, checks them for an id attribute,
541 * and calls applyTo on the existing dom element with that id.
542 * @return {BasicForm} this
545 this.items.each(function(f){
546 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
554 * Calls {@link Ext#apply} for all fields in this form with the passed object.
555 * @param {Object} values
556 * @return {BasicForm} this
558 applyToFields : function(o){
559 this.items.each(function(f){
566 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
567 * @param {Object} values
568 * @return {BasicForm} this
570 applyIfToFields : function(o){
571 this.items.each(function(f){
579 Roo.BasicForm = Roo.form.BasicForm;