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.Form
14 * @extends Roo.form.BasicForm
15 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
16 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
18 * @param {Object} config Configuration options
20 Roo.form.Form = function(config){
23 xitems = config.items;
28 Roo.form.Form.superclass.constructor.call(this, null, config);
29 this.url = this.url || this.action;
31 this.root = new Roo.form.Layout(Roo.applyIf({
35 this.active = this.root;
37 * Array of all the buttons that have been added to this form via {@link addButton}
44 * @event clientvalidation
45 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47 * @param {Boolean} valid true if the form has passed client-side validation
49 clientvalidation: true,
52 * Fires when the form is rendered
53 * @param {Roo.form.Form} form
58 if (this.progressUrl) {
59 // push a hidden field onto the list of fields..
63 name : 'UPLOAD_IDENTIFIER'
68 Roo.each(xitems, this.addxtype, this);
72 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
74 * @cfg {Roo.Button} buttons[] buttons at bottom of form
78 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
81 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
84 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
89 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
94 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
95 * This property cascades to child containers if not set.
100 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
101 * fires a looping event with that state. This is required to bind buttons to the valid
102 * state using the config value formBind:true on the button.
104 monitorValid : false,
107 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
112 * @cfg {String} progressUrl - Url to return progress data
117 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
118 * sending a formdata with extra parameters - eg uploaded elements.
124 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
125 * fields are added and the column is closed. If no fields are passed the column remains open
126 * until end() is called.
127 * @param {Object} config The config to pass to the column
128 * @param {Field} field1 (optional)
129 * @param {Field} field2 (optional)
130 * @param {Field} etc (optional)
131 * @return Column The column container object
133 column : function(c){
134 var col = new Roo.form.Column(c);
136 if(arguments.length > 1){ // duplicate code required because of Opera
137 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
144 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
145 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
146 * until end() is called.
147 * @param {Object} config The config to pass to the fieldset
148 * @param {Field} field1 (optional)
149 * @param {Field} field2 (optional)
150 * @param {Field} etc (optional)
151 * @return FieldSet The fieldset container object
153 fieldset : function(c){
154 var fs = new Roo.form.FieldSet(c);
156 if(arguments.length > 1){ // duplicate code required because of Opera
157 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
164 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
165 * fields are added and the container is closed. If no fields are passed the container remains open
166 * until end() is called.
167 * @param {Object} config The config to pass to the Layout
168 * @param {Field} field1 (optional)
169 * @param {Field} field2 (optional)
170 * @param {Field} etc (optional)
171 * @return Layout The container object
173 container : function(c){
174 var l = new Roo.form.Layout(c);
176 if(arguments.length > 1){ // duplicate code required because of Opera
177 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
184 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
185 * @param {Object} container A Roo.form.Layout or subclass of Layout
186 * @return {Form} this
189 // cascade label info
190 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
191 this.active.stack.push(c);
192 c.ownerCt = this.active;
198 * Closes the current open container
199 * @return {Form} this
202 if(this.active == this.root){
205 this.active = this.active.ownerCt;
210 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
211 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
212 * as the label of the field.
213 * @param {Field} field1
214 * @param {Field} field2 (optional)
215 * @param {Field} etc. (optional)
216 * @return {Form} this
219 this.active.stack.push.apply(this.active.stack, arguments);
220 this.allItems.push.apply(this.allItems,arguments);
222 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
223 if(a[i].isFormField){
228 Roo.form.Form.superclass.add.apply(this, r);
238 * Find any element that has been added to a form, using it's ID or name
239 * This can include framesets, columns etc. along with regular fields..
240 * @param {String} id - id or name to find.
242 * @return {Element} e - or false if nothing found.
244 findbyId : function(id)
250 Roo.each(this.allItems, function(f){
251 if (f.id == id || f.name == id ){
262 * Render this form into the passed container. This should only be called once!
263 * @param {String/HTMLElement/Element} container The element this component should be rendered into
264 * @return {Form} this
266 render : function(ct)
272 var o = this.autoCreate || {
274 method : this.method || 'POST',
275 id : this.id || Roo.id()
277 this.initEl(ct.createChild(o));
279 this.root.render(this.el);
283 this.items.each(function(f){
284 f.render('x-form-el-'+f.id);
287 if(this.buttons.length > 0){
288 // tables are required to maintain order and for correct IE layout
289 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
290 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
291 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
293 var tr = tb.getElementsByTagName('tr')[0];
294 for(var i = 0, len = this.buttons.length; i < len; i++) {
295 var b = this.buttons[i];
296 var td = document.createElement('td');
297 td.className = 'x-form-btn-td';
298 b.render(tr.appendChild(td));
301 if(this.monitorValid){ // initialize after render
302 this.startMonitoring();
304 this.fireEvent('rendered', this);
309 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
310 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
311 * object or a valid Roo.DomHelper element config
312 * @param {Function} handler The function called when the button is clicked
313 * @param {Object} scope (optional) The scope of the handler function
314 * @return {Roo.Button}
316 addButton : function(config, handler, scope){
320 minWidth: this.minButtonWidth,
323 if(typeof config == "string"){
326 Roo.apply(bc, config);
328 var btn = new Roo.Button(null, bc);
329 this.buttons.push(btn);
334 * Adds a series of form elements (using the xtype property as the factory method.
335 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
336 * @param {Object} config
339 addxtype : function()
341 var ar = Array.prototype.slice.call(arguments, 0);
343 for(var i = 0; i < ar.length; i++) {
345 continue; // skip -- if this happends something invalid got sent, we
346 // should ignore it, as basically that interface element will not show up
347 // and that should be pretty obvious!!
350 if (Roo.form[ar[i].xtype]) {
352 var fe = Roo.factory(ar[i], Roo.form);
358 fe.store.form = this;
363 this.allItems.push(fe);
364 if (fe.items && fe.addxtype) {
365 fe.addxtype.apply(fe, fe.items);
375 // console.log('adding ' + ar[i].xtype);
377 if (ar[i].xtype == 'Button') {
378 //console.log('adding button');
379 //console.log(ar[i]);
380 this.addButton(ar[i]);
381 this.allItems.push(fe);
385 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
386 alert('end is not supported on xtype any more, use items');
388 // //console.log('adding end');
396 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
397 * option "monitorValid"
399 startMonitoring : function(){
403 run : this.bindHandler,
404 interval : this.monitorPoll || 200,
411 * Stops monitoring of the valid state of this form
413 stopMonitoring : function(){
418 bindHandler : function(){
420 return false; // stops binding
423 this.items.each(function(f){
424 if(!f.isValid(true)){
429 for(var i = 0, len = this.buttons.length; i < len; i++){
430 var btn = this.buttons[i];
431 if(btn.formBind === true && btn.disabled === valid){
432 btn.setDisabled(!valid);
435 this.fireEvent('clientvalidation', this, valid);
449 Roo.Form = Roo.form.Form;