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'}.
92 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
100 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
101 * or setValues() data instead of when the form was first created.
103 trackResetOnLoad : false,
107 * childForms - used for multi-tab forms
113 * allItems - full list of fields.
119 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
120 * element by passing it or its id or mask the form itself by passing in true.
123 waitMsgTarget : undefined,
126 initEl : function(el){
127 this.el = Roo.get(el);
128 this.id = this.el.id || Roo.id();
129 this.el.on('submit', this.onSubmit, this);
130 this.el.addClass('x-form');
134 onSubmit : function(e){
139 * Returns true if client-side validation on the form is successful.
142 isValid : function(){
144 this.items.each(function(f){
153 * Returns true if any fields in this form have changed since their original load.
156 isDirty : function(){
158 this.items.each(function(f){
168 * Performs a predefined action (submit or load) or custom actions you define on this form.
169 * @param {String} actionName The name of the action type
170 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
171 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
172 * accept other config options):
174 Property Type Description
175 ---------------- --------------- ----------------------------------------------------------------------------------
176 url String The url for the action (defaults to the form's url)
177 method String The form method to use (defaults to the form's method, or POST if not defined)
178 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
179 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
180 validate the form on the client (defaults to false)
182 * @return {BasicForm} this
184 doAction : function(action, options){
185 if(typeof action == 'string'){
186 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
188 if(this.fireEvent('beforeaction', this, action) !== false){
189 this.beforeAction(action);
190 action.run.defer(100, action);
196 * Shortcut to do a submit action.
197 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
198 * @return {BasicForm} this
200 submit : function(options){
201 this.doAction('submit', options);
206 * Shortcut to do a load action.
207 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
208 * @return {BasicForm} this
210 load : function(options){
211 this.doAction('load', options);
216 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
217 * @param {Record} record The record to edit
218 * @return {BasicForm} this
220 updateRecord : function(record){
222 var fs = record.fields;
224 var field = this.findField(f.name);
226 record.set(f.name, field.getValue());
234 * Loads an Roo.data.Record into this form.
235 * @param {Record} record The record to load
236 * @return {BasicForm} this
238 loadRecord : function(record){
239 this.setValues(record.data);
244 beforeAction : function(action){
245 var o = action.options;
248 o.waitMsgTarget = this.dialog.el;
251 if(this.waitMsgTarget === true){
252 this.el.mask(o.waitMsg, 'x-mask-loading');
253 }else if(this.waitMsgTarget){
254 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
255 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
257 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
263 afterAction : function(action, success){
264 this.activeAction = null;
265 var o = action.options;
267 if(this.waitMsgTarget === true){
269 }else if(this.waitMsgTarget){
270 this.waitMsgTarget.unmask();
272 Roo.MessageBox.updateProgress(1);
273 Roo.MessageBox.hide();
280 Roo.callback(o.success, o.scope, [this, action]);
281 this.fireEvent('actioncomplete', this, action);
284 Roo.callback(o.failure, o.scope, [this, action]);
285 // show an error message if no failed handler is set..
286 if (!this.hasListener('actionfailed')) {
287 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
290 this.fireEvent('actionfailed', this, action);
296 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
297 * @param {String} id The value to search for
300 findField : function(id){
301 var field = this.items.get(id);
303 this.items.each(function(f){
304 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
310 return field || null;
314 * Add a secondary form to this one,
315 * Used to provide tabbed forms. One form is primary, with hidden values
316 * which mirror the elements from the other forms.
318 * @param {Roo.form.Form} form to add.
321 addForm : function(form)
324 if (this.childForms.indexOf(form) > -1) {
328 this.childForms.push(form);
330 Roo.each(form.allItems, function (fe) {
332 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
333 if (this.findField(n)) { // already added..
336 var add = new Roo.form.Hidden({
346 * Mark fields in this form invalid in bulk.
347 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
348 * @return {BasicForm} this
350 markInvalid : function(errors){
351 if(errors instanceof Array){
352 for(var i = 0, len = errors.length; i < len; i++){
353 var fieldError = errors[i];
354 var f = this.findField(fieldError.id);
356 f.markInvalid(fieldError.msg);
362 if(typeof errors[id] != 'function' && (field = this.findField(id))){
363 field.markInvalid(errors[id]);
367 Roo.each(this.childForms || [], function (f) {
368 f.markInvalid(errors);
375 * Set values for fields in this form in bulk.
376 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
377 * @return {BasicForm} this
379 setValues : function(values){
380 if(values instanceof Array){ // array of objects
381 for(var i = 0, len = values.length; i < len; i++){
383 var f = this.findField(v.id);
386 if(this.trackResetOnLoad){
387 f.originalValue = f.getValue();
391 }else{ // object hash
394 if(typeof values[id] != 'function' && (field = this.findField(id))){
396 if (field.setFromData &&
398 field.displayField &&
399 // combos' with local stores can
400 // be queried via setValue()
401 // to set their value..
402 (field.store && !field.store.isLocal)
406 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
407 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
408 field.setFromData(sd);
411 field.setValue(values[id]);
415 if(this.trackResetOnLoad){
416 field.originalValue = field.getValue();
422 Roo.each(this.childForms || [], function (f) {
430 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
431 * they are returned as an array.
432 * @param {Boolean} asString
435 getValues : function(asString){
436 if (this.childForms) {
437 // copy values from the child forms
438 Roo.each(this.childForms, function (f) {
439 this.setValues(f.getValues());
445 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
446 if(asString === true){
449 return Roo.urlDecode(fs);
453 * Returns the fields in this form as an object with key/value pairs.
454 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
457 getFieldValues : function()
459 if (this.childForms) {
460 // copy values from the child forms
461 Roo.each(this.childForms, function (f) {
462 this.setValues(f.getValues());
467 this.items.each(function(f){
471 var v = f.getValue();
472 if ((typeof(v) == 'object') && f.getRawValue) {
473 v = f.getRawValue() ; // dates..
475 ret[f.getName()] = v;
482 * Clears all invalid messages in this form.
483 * @return {BasicForm} this
485 clearInvalid : function(){
486 this.items.each(function(f){
490 Roo.each(this.childForms || [], function (f) {
500 * @return {BasicForm} this
503 this.items.each(function(f){
507 Roo.each(this.childForms || [], function (f) {
516 * Add Roo.form components to this form.
517 * @param {Field} field1
518 * @param {Field} field2 (optional)
519 * @param {Field} etc (optional)
520 * @return {BasicForm} this
523 this.items.addAll(Array.prototype.slice.call(arguments, 0));
529 * Removes a field from the items collection (does NOT remove its markup).
530 * @param {Field} field
531 * @return {BasicForm} this
533 remove : function(field){
534 this.items.remove(field);
539 * Looks at the fields in this form, checks them for an id attribute,
540 * and calls applyTo on the existing dom element with that id.
541 * @return {BasicForm} this
544 this.items.each(function(f){
545 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
553 * Calls {@link Ext#apply} for all fields in this form with the passed object.
554 * @param {Object} values
555 * @return {BasicForm} this
557 applyToFields : function(o){
558 this.items.each(function(f){
565 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
566 * @param {Object} values
567 * @return {BasicForm} this
569 applyIfToFields : function(o){
570 this.items.each(function(f){
578 Roo.BasicForm = Roo.form.BasicForm;