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,
132 initEl : function(el){
133 this.el = Roo.get(el);
134 this.id = this.el.id || Roo.id();
135 this.el.on('submit', this.onSubmit, this);
136 this.el.addClass('x-form');
140 onSubmit : function(e){
145 * Returns true if client-side validation on the form is successful.
148 isValid : function(){
150 this.items.each(function(f){
159 * DEPRICATED Returns true if any fields in this form have changed since their original load.
162 isDirty : function(){
164 this.items.each(function(f){
174 * Returns true if any fields in this form have changed since their original load. (New version)
178 hasChanged : function()
181 this.items.each(function(f){
191 * Resets all hasChanged to 'false' -
192 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
193 * So hasChanged storage is only to be used for this purpose
196 resetHasChanged : function()
198 this.items.each(function(f){
206 * Performs a predefined action (submit or load) or custom actions you define on this form.
207 * @param {String} actionName The name of the action type
208 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
209 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
210 * accept other config options):
212 Property Type Description
213 ---------------- --------------- ----------------------------------------------------------------------------------
214 url String The url for the action (defaults to the form's url)
215 method String The form method to use (defaults to the form's method, or POST if not defined)
216 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
217 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
218 validate the form on the client (defaults to false)
220 * @return {BasicForm} this
222 doAction : function(action, options){
223 if(typeof action == 'string'){
224 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
226 if(this.fireEvent('beforeaction', this, action) !== false){
227 this.beforeAction(action);
228 action.run.defer(100, action);
234 * Shortcut to do a submit action.
235 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
236 * @return {BasicForm} this
238 submit : function(options){
239 this.doAction('submit', options);
244 * Shortcut to do a load action.
245 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
246 * @return {BasicForm} this
248 load : function(options){
249 this.doAction('load', options);
254 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
255 * @param {Record} record The record to edit
256 * @return {BasicForm} this
258 updateRecord : function(record){
260 var fs = record.fields;
262 var field = this.findField(f.name);
264 record.set(f.name, field.getValue());
272 * Loads an Roo.data.Record into this form.
273 * @param {Record} record The record to load
274 * @return {BasicForm} this
276 loadRecord : function(record){
277 this.setValues(record.data);
282 beforeAction : function(action){
283 var o = action.options;
285 if(!this.disableMask) {
286 if(this.waitMsgTarget === true){
287 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
288 }else if(this.waitMsgTarget){
289 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
290 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
292 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
300 afterAction : function(action, success){
301 this.activeAction = null;
302 var o = action.options;
304 if(!this.disableMask) {
305 if(this.waitMsgTarget === true){
307 }else if(this.waitMsgTarget){
308 this.waitMsgTarget.unmask();
310 Roo.MessageBox.updateProgress(1);
311 Roo.MessageBox.hide();
319 Roo.callback(o.success, o.scope, [this, action]);
320 this.fireEvent('actioncomplete', this, action);
324 // failure condition..
325 // we have a scenario where updates need confirming.
326 // eg. if a locking scenario exists..
327 // we look for { errors : { needs_confirm : true }} in the response.
329 (typeof(action.result) != 'undefined') &&
330 (typeof(action.result.errors) != 'undefined') &&
331 (typeof(action.result.errors.needs_confirm) != 'undefined')
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.MessageBox.alert("Error",
355 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
356 action.result.errorMsg :
357 "Saving Failed, please check your entries or try again"
361 this.fireEvent('actionfailed', this, action);
367 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
368 * @param {String} id The value to search for
371 findField : function(id){
372 var field = this.items.get(id);
374 this.items.each(function(f){
375 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
381 return field || null;
385 * Add a secondary form to this one,
386 * Used to provide tabbed forms. One form is primary, with hidden values
387 * which mirror the elements from the other forms.
389 * @param {Roo.form.Form} form to add.
392 addForm : function(form)
395 if (this.childForms.indexOf(form) > -1) {
399 this.childForms.push(form);
401 Roo.each(form.allItems, function (fe) {
403 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
404 if (this.findField(n)) { // already added..
407 var add = new Roo.form.Hidden({
417 * Mark fields in this form invalid in bulk.
418 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
419 * @return {BasicForm} this
421 markInvalid : function(errors){
422 if(errors instanceof Array){
423 for(var i = 0, len = errors.length; i < len; i++){
424 var fieldError = errors[i];
425 var f = this.findField(fieldError.id);
427 f.markInvalid(fieldError.msg);
433 if(typeof errors[id] != 'function' && (field = this.findField(id))){
434 field.markInvalid(errors[id]);
438 Roo.each(this.childForms || [], function (f) {
439 f.markInvalid(errors);
446 * Set values for fields in this form in bulk.
447 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
448 * @return {BasicForm} this
450 setValues : function(values){
451 if(values instanceof Array){ // array of objects
452 for(var i = 0, len = values.length; i < len; i++){
454 var f = this.findField(v.id);
457 if(this.trackResetOnLoad){
458 f.originalValue = f.getValue();
462 }else{ // object hash
465 if(typeof values[id] != 'function' && (field = this.findField(id))){
467 if (field.setFromData &&
469 field.displayField &&
470 // combos' with local stores can
471 // be queried via setValue()
472 // to set their value..
473 (field.store && !field.store.isLocal)
477 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
478 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
479 field.setFromData(sd);
482 field.setValue(values[id]);
486 if(this.trackResetOnLoad){
487 field.originalValue = field.getValue();
492 this.resetHasChanged();
495 Roo.each(this.childForms || [], function (f) {
504 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
505 * they are returned as an array.
506 * @param {Boolean} asString
509 getValues : function(asString){
510 if (this.childForms) {
511 // copy values from the child forms
512 Roo.each(this.childForms, function (f) {
513 this.setValues(f.getValues());
519 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
520 if(asString === true){
523 return Roo.urlDecode(fs);
527 * Returns the fields in this form as an object with key/value pairs.
528 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
531 getFieldValues : function(with_hidden)
533 if (this.childForms) {
534 // copy values from the child forms
535 // should this call getFieldValues - probably not as we do not currently copy
536 // hidden fields when we generate..
537 Roo.each(this.childForms, function (f) {
538 this.setValues(f.getValues());
543 this.items.each(function(f){
547 var v = f.getValue();
548 if (f.inputType =='radio') {
549 if (typeof(ret[f.getName()]) == 'undefined') {
550 ret[f.getName()] = ''; // empty..
553 if (!f.el.dom.checked) {
561 // not sure if this supported any more..
562 if ((typeof(v) == 'object') && f.getRawValue) {
563 v = f.getRawValue() ; // dates..
565 // combo boxes where name != hiddenName...
566 if (f.name != f.getName()) {
567 ret[f.name] = f.getRawValue();
569 ret[f.getName()] = v;
576 * Clears all invalid messages in this form.
577 * @return {BasicForm} this
579 clearInvalid : function(){
580 this.items.each(function(f){
584 Roo.each(this.childForms || [], function (f) {
594 * @return {BasicForm} this
597 this.items.each(function(f){
601 Roo.each(this.childForms || [], function (f) {
604 this.resetHasChanged();
610 * Add Roo.form components to this form.
611 * @param {Field} field1
612 * @param {Field} field2 (optional)
613 * @param {Field} etc (optional)
614 * @return {BasicForm} this
617 this.items.addAll(Array.prototype.slice.call(arguments, 0));
623 * Removes a field from the items collection (does NOT remove its markup).
624 * @param {Field} field
625 * @return {BasicForm} this
627 remove : function(field){
628 this.items.remove(field);
633 * Looks at the fields in this form, checks them for an id attribute,
634 * and calls applyTo on the existing dom element with that id.
635 * @return {BasicForm} this
638 this.items.each(function(f){
639 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
647 * Calls {@link Ext#apply} for all fields in this form with the passed object.
648 * @param {Object} values
649 * @return {BasicForm} this
651 applyToFields : function(o){
652 this.items.each(function(f){
659 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
660 * @param {Object} values
661 * @return {BasicForm} this
663 applyIfToFields : function(o){
664 this.items.each(function(f){
672 Roo.BasicForm = Roo.form.BasicForm;