X-Git-Url: http://git.roojs.org/?a=blobdiff_plain;f=Roo%2Fform%2FBasicForm.js;h=911f00b1f752cde4541f041948687aca86afd68b;hb=15bec6bc4f4d375adc17530ee49d22a55e5d9ba4;hp=edc42b5a14315a35cba87a1b195ba98a394e1268;hpb=ddac93ca8c988cf257da5b589e1b23302ce2e077;p=roojs1 diff --git a/Roo/form/BasicForm.js b/Roo/form/BasicForm.js index edc42b5a14..911f00b1f7 100644 --- a/Roo/form/BasicForm.js +++ b/Roo/form/BasicForm.js @@ -18,11 +18,15 @@ * @param {Object} config Configuration options */ Roo.form.BasicForm = function(el, config){ + this.allItems = []; + this.childForms = []; Roo.apply(this, config); /* * The Roo.form.Field items in this form. * @type MixedCollection */ + + this.items = new Roo.util.MixedCollection(false, function(o){ return o.id || (o.id = Roo.id()); }); @@ -53,6 +57,8 @@ Roo.form.BasicForm = function(el, config){ this.initEl(el); } Roo.form.BasicForm.superclass.constructor.call(this); + + Roo.form.BasicForm.popover.apply(); }; Roo.extend(Roo.form.BasicForm, Roo.util.Observable, { @@ -78,10 +84,13 @@ Roo.extend(Roo.form.BasicForm, Roo.util.Observable, { * @cfg {Boolean} fileUpload * Set to true if this form is a file upload. */ + /** * @cfg {Object} baseParams * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}. */ + /** + /** * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds). */ @@ -95,20 +104,41 @@ Roo.extend(Roo.form.BasicForm, Roo.util.Observable, { * or setValues() data instead of when the form was first created. */ trackResetOnLoad : false, - + /** - * allFields - full list of fields. + * childForms - used for multi-tab forms * @type {Array} */ - allFields : false, + childForms : false, + + /** + * allItems - full list of fields. + * @type {Array} + */ + allItems : false, /** * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific * element by passing it or its id or mask the form itself by passing in true. * @type Mixed */ - waitMsgTarget : undefined, + waitMsgTarget : false, + + /** + * @type Boolean + */ + disableMask : false, + + /** + * @cfg {Boolean} errorMask (true|false) default false + */ + errorMask : false, + + /** + * @cfg {Number} maskOffset Default 100 + */ + maskOffset : 100, // private initEl : function(el){ @@ -129,16 +159,28 @@ Roo.extend(Roo.form.BasicForm, Roo.util.Observable, { */ isValid : function(){ var valid = true; + var target = false; this.items.each(function(f){ - if(!f.validate()){ - valid = false; - } + if(f.validate()){ + return; + } + + valid = false; + + if(!target && f.el.isVisible(true)){ + target = f; + } }); + + if(this.errorMask && !valid){ + Roo.form.BasicForm.popover.mask(this, target); + } + return valid; }, /** - * Returns true if any fields in this form have changed since their original load. + * DEPRICATED Returns true if any fields in this form have changed since their original load. * @return Boolean */ isDirty : function(){ @@ -151,7 +193,39 @@ Roo.extend(Roo.form.BasicForm, Roo.util.Observable, { }); return dirty; }, - + + /** + * Returns true if any fields in this form have changed since their original load. (New version) + * @return Boolean + */ + + hasChanged : function() + { + var dirty = false; + this.items.each(function(f){ + if(f.hasChanged()){ + dirty = true; + return false; + } + }); + return dirty; + + }, + /** + * Resets all hasChanged to 'false' - + * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things. + * So hasChanged storage is only to be used for this purpose + * @return Boolean + */ + resetHasChanged : function() + { + this.items.each(function(f){ + f.resetHasChanged(); + }); + + }, + + /** * Performs a predefined action (submit or load) or custom actions you define on this form. * @param {String} actionName The name of the action type @@ -231,23 +305,27 @@ clientValidation Boolean Applies to submit only. Pass true to call fo // private beforeAction : function(action){ var o = action.options; - if(o.waitMsg){ + + if(!this.disableMask) { if(this.waitMsgTarget === true){ - this.el.mask(o.waitMsg, 'x-mask-loading'); + this.el.mask(o.waitMsg || "Sending", 'x-mask-loading'); }else if(this.waitMsgTarget){ this.waitMsgTarget = Roo.get(this.waitMsgTarget); - this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading'); - }else{ - Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...'); + this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading'); + }else { + Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...'); } } + + }, // private afterAction : function(action, success){ this.activeAction = null; var o = action.options; - if(o.waitMsg){ + + if(!this.disableMask) { if(this.waitMsgTarget === true){ this.el.unmask(); }else if(this.waitMsgTarget){ @@ -257,16 +335,56 @@ clientValidation Boolean Applies to submit only. Pass true to call fo Roo.MessageBox.hide(); } } + if(success){ if(o.reset){ this.reset(); } Roo.callback(o.success, o.scope, [this, action]); this.fireEvent('actioncomplete', this, action); + }else{ + + // failure condition.. + // we have a scenario where updates need confirming. + // eg. if a locking scenario exists.. + // we look for { errors : { needs_confirm : true }} in the response. + if ( + (typeof(action.result) != 'undefined') && + (typeof(action.result.errors) != 'undefined') && + (typeof(action.result.errors.needs_confirm) != 'undefined') + ){ + var _t = this; + Roo.MessageBox.confirm( + "Change requires confirmation", + action.result.errorMsg, + function(r) { + if (r != 'yes') { + return; + } + _t.doAction('submit', { params : { _submit_confirmed : 1 } } ); + } + + ); + + + + return; + } + Roo.callback(o.failure, o.scope, [this, action]); + // show an error message if no failed handler is set.. + if (!this.hasListener('actionfailed')) { + Roo.MessageBox.alert("Error", + (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ? + action.result.errorMsg : + "Saving Failed, please check your entries or try again" + ); + } + this.fireEvent('actionfailed', this, action); } + }, /** @@ -287,7 +405,38 @@ clientValidation Boolean Applies to submit only. Pass true to call fo return field || null; }, - + /** + * Add a secondary form to this one, + * Used to provide tabbed forms. One form is primary, with hidden values + * which mirror the elements from the other forms. + * + * @param {Roo.form.Form} form to add. + * + */ + addForm : function(form) + { + + if (this.childForms.indexOf(form) > -1) { + // already added.. + return; + } + this.childForms.push(form); + var n = ''; + Roo.each(form.allItems, function (fe) { + + n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName(); + if (this.findField(n)) { // already added.. + return; + } + var add = new Roo.form.Hidden({ + name : n + }); + add.render(this.el); + + this.add( add ); + }, this); + + }, /** * Mark fields in this form invalid in bulk. * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2} @@ -310,6 +459,10 @@ clientValidation Boolean Applies to submit only. Pass true to call fo } } } + Roo.each(this.childForms || [], function (f) { + f.markInvalid(errors); + }); + return this; }, @@ -360,12 +513,14 @@ clientValidation Boolean Applies to submit only. Pass true to call fo } } } - if (this.childForms) { - Roo.each(this.childForms, function (f) { - f.setValues(values); - }); - } + this.resetHasChanged(); + + Roo.each(this.childForms || [], function (f) { + f.setValues(values); + f.resetHasChanged(); + }); + return this; }, @@ -379,16 +534,22 @@ clientValidation Boolean Applies to submit only. Pass true to call fo if (this.childForms) { // copy values from the child forms Roo.each(this.childForms, function (f) { - if (f.allFields) { - Roo.each(f.allFields, function (e) { - if (e.name && e.getValue && this.findField(e.name)) { - this.findField(e.name).setValue(e.getValue()); - } - }); - } + this.setValues(f.getValues()); }, this); } + // use formdata + if (typeof(FormData) != 'undefined' && asString !== true) { + var fd = new FormData(this.el.dom).entries(); + var ret = {}; + while (pair = fd.next()) { + if (pair.done) { + break; + } + ret[pair.value[0]] = pair.value[1]; // not sure how this will handle duplicates.. + }; + return ret; + } var fs = Roo.lib.Ajax.serializeForm(this.el.dom); @@ -397,6 +558,55 @@ clientValidation Boolean Applies to submit only. Pass true to call fo } return Roo.urlDecode(fs); }, + + /** + * Returns the fields in this form as an object with key/value pairs. + * This differs from getValues as it calls getValue on each child item, rather than using dom data. + * @return {Object} + */ + getFieldValues : function(with_hidden) + { + if (this.childForms) { + // copy values from the child forms + // should this call getFieldValues - probably not as we do not currently copy + // hidden fields when we generate.. + Roo.each(this.childForms, function (f) { + this.setValues(f.getValues()); + }, this); + } + + var ret = {}; + this.items.each(function(f){ + if (!f.getName()) { + return; + } + var v = f.getValue(); + if (f.inputType =='radio') { + if (typeof(ret[f.getName()]) == 'undefined') { + ret[f.getName()] = ''; // empty.. + } + + if (!f.el.dom.checked) { + return; + + } + v = f.el.dom.value; + + } + + // not sure if this supported any more.. + if ((typeof(v) == 'object') && f.getRawValue) { + v = f.getRawValue() ; // dates.. + } + // combo boxes where name != hiddenName... + if (f.name != f.getName()) { + ret[f.name] = f.getRawValue(); + } + ret[f.getName()] = v; + }); + + return ret; + }, /** * Clears all invalid messages in this form. @@ -406,6 +616,12 @@ clientValidation Boolean Applies to submit only. Pass true to call fo this.items.each(function(f){ f.clearInvalid(); }); + + Roo.each(this.childForms || [], function (f) { + f.clearInvalid(); + }); + + return this; }, @@ -417,11 +633,11 @@ clientValidation Boolean Applies to submit only. Pass true to call fo this.items.each(function(f){ f.reset(); }); - if (this.childForms) { - Roo.each(this.childForms, function (f) { - f.reset(); - }); - } + + Roo.each(this.childForms || [], function (f) { + f.reset(); + }); + this.resetHasChanged(); return this; }, @@ -489,4 +705,150 @@ clientValidation Boolean Applies to submit only. Pass true to call fo }); // back compat -Roo.BasicForm = Roo.form.BasicForm; \ No newline at end of file +Roo.BasicForm = Roo.form.BasicForm; + +Roo.apply(Roo.form.BasicForm, { + + popover : { + + padding : 5, + + isApplied : false, + + isMasked : false, + + form : false, + + target : false, + + intervalID : false, + + maskEl : false, + + apply : function() + { + if(this.isApplied){ + return; + } + + this.maskEl = { + top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true), + left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true), + bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true), + right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true) + }; + + this.maskEl.top.enableDisplayMode("block"); + this.maskEl.left.enableDisplayMode("block"); + this.maskEl.bottom.enableDisplayMode("block"); + this.maskEl.right.enableDisplayMode("block"); + + Roo.get(document.body).on('click', function(){ + this.unmask(); + }, this); + + Roo.get(document.body).on('touchstart', function(){ + this.unmask(); + }, this); + + this.isApplied = true + }, + + mask : function(form, target) + { + this.form = form; + + this.target = target; + + if(!this.form.errorMask || !target.el){ + return; + } + + var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body); + + var ot = this.target.el.calcOffsetsTo(scrollable); + + var scrollTo = ot[1] - this.form.maskOffset; + + scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight); + + scrollable.scrollTo('top', scrollTo); + + var el = this.target.wrap || this.target.el; + + var box = el.getBox(); + + this.maskEl.top.setStyle('position', 'absolute'); + this.maskEl.top.setStyle('z-index', 10000); + this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding); + this.maskEl.top.setLeft(0); + this.maskEl.top.setTop(0); + this.maskEl.top.show(); + + this.maskEl.left.setStyle('position', 'absolute'); + this.maskEl.left.setStyle('z-index', 10000); + this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2); + this.maskEl.left.setLeft(0); + this.maskEl.left.setTop(box.y - this.padding); + this.maskEl.left.show(); + + this.maskEl.bottom.setStyle('position', 'absolute'); + this.maskEl.bottom.setStyle('z-index', 10000); + this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding); + this.maskEl.bottom.setLeft(0); + this.maskEl.bottom.setTop(box.bottom + this.padding); + this.maskEl.bottom.show(); + + this.maskEl.right.setStyle('position', 'absolute'); + this.maskEl.right.setStyle('z-index', 10000); + this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2); + this.maskEl.right.setLeft(box.right + this.padding); + this.maskEl.right.setTop(box.y - this.padding); + this.maskEl.right.show(); + + this.intervalID = window.setInterval(function() { + Roo.form.BasicForm.popover.unmask(); + }, 10000); + + window.onwheel = function(){ return false;}; + + (function(){ this.isMasked = true; }).defer(500, this); + + }, + + unmask : function() + { + if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){ + return; + } + + this.maskEl.top.setStyle('position', 'absolute'); + this.maskEl.top.setSize(0, 0).setXY([0, 0]); + this.maskEl.top.hide(); + + this.maskEl.left.setStyle('position', 'absolute'); + this.maskEl.left.setSize(0, 0).setXY([0, 0]); + this.maskEl.left.hide(); + + this.maskEl.bottom.setStyle('position', 'absolute'); + this.maskEl.bottom.setSize(0, 0).setXY([0, 0]); + this.maskEl.bottom.hide(); + + this.maskEl.right.setStyle('position', 'absolute'); + this.maskEl.right.setSize(0, 0).setXY([0, 0]); + this.maskEl.right.hide(); + + window.onwheel = function(){ return true;}; + + if(this.intervalID){ + window.clearInterval(this.intervalID); + this.intervalID = false; + } + + this.isMasked = false; + + } + + } + +}); \ No newline at end of file