From 0c6f70005a85d4c925664f4762cddc09410f5a93 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 26 Jun 2013 20:50:10 +0000 Subject: [PATCH] Implement some basic client-side validation (required) --- etemplate/js/et2_core_baseWidget.js | 9 +++-- etemplate/js/et2_core_inputWidget.js | 44 +++++++++++++++++++--- etemplate/js/et2_core_interfaces.js | 18 ++++++++- etemplate/js/et2_extension_customfields.js | 7 +++- etemplate/js/et2_widget_button.js | 3 ++ etemplate/js/et2_widget_link.js | 2 +- etemplate/js/et2_widget_radiobox.js | 6 +-- etemplate/js/et2_widget_selectAccount.js | 2 +- etemplate/js/et2_widget_tabs.js | 3 ++ etemplate/js/et2_widget_textbox.js | 2 +- etemplate/js/etemplate2.js | 14 +------ 11 files changed, 81 insertions(+), 29 deletions(-) diff --git a/etemplate/js/et2_core_baseWidget.js b/etemplate/js/et2_core_baseWidget.js index 3f98051ac1..e8d108a20e 100644 --- a/etemplate/js/et2_core_baseWidget.js +++ b/etemplate/js/et2_core_baseWidget.js @@ -112,7 +112,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, .addClass("message") .addClass(_type) .addClass(_floating ? "floating" : "") - .text(_text); + .text(_text.valueOf() + ""); // Decide whether to prepend or append the div if (_prepend) @@ -152,10 +152,11 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, { var surr = this.getSurroundings(); var self = this; + var messageDiv = this._messageDiv; + self._messageDiv = null; var _done = function() { - surr.removeDOMNode(self._messageDiv[0]); - self._messageDiv = null; + surr.removeDOMNode(messageDiv[0]); // Update the surroundings manager if (!_noUpdate) @@ -167,7 +168,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, // Either fade out or directly call the function which removes the div if (_fade) { - this._messageDiv.fadeOut("fast", _done); + messageDiv.fadeOut("fast", _done); } else { diff --git a/etemplate/js/et2_core_inputWidget.js b/etemplate/js/et2_core_inputWidget.js index e21bcf1c85..9babe580c0 100644 --- a/etemplate/js/et2_core_inputWidget.js +++ b/etemplate/js/et2_core_inputWidget.js @@ -25,10 +25,10 @@ * * @augments et2_valueWidget */ -var et2_inputWidget = et2_valueWidget.extend(et2_IInput, +var et2_inputWidget = et2_valueWidget.extend([et2_IInput,et2_ISubmitListener], { attributes: { - "required": { + "needed": { "name": "Required", "default": false, "type": "boolean", @@ -124,10 +124,17 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, }, change: function(_node) { - if (this.onchange) + var messages = []; + var valid = this.isValid(messages); + + // Passing false will clear any set messages + this.set_validation_error(valid ? false : messages); + + if (valid && this.onchange) { return et2_compileLegacyJS(this.onchange, this, _node)(); } + return valid; }, set_value: function(_value) { @@ -218,7 +225,7 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, this.label = _value; }, - set_required: function(_value) { + set_needed: function(_value) { var node = this.getInputNode(); if (node) { @@ -281,7 +288,34 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, resetDirty: function() { this._oldValue = this.getValue(); - } + }, + + isValid: function(messages) { + var ok = true; + + // Check for required + if(this.options.needed && (this.getValue() == null || this.getValue().valueOf() == '')) + { + messages.push(this.egw().lang('input required')); + ok = false; + } + return ok; + }, + /** + * Called whenever the template gets submitted. We return false if the widget + * is not valid, which cancels the submission. + * + * @param _values contains the values which will be sent to the server. + * Listeners may change these values before they get submitted. + */ + submit: function(_values) { + var messages = []; + var valid = this.isValid(messages); + + // Passing false will clear any set messages + this.set_validation_error(valid ? false : messages); + return valid; + } }); diff --git a/etemplate/js/et2_core_interfaces.js b/etemplate/js/et2_core_interfaces.js index 749f16821e..32c0760d9a 100644 --- a/etemplate/js/et2_core_interfaces.js +++ b/etemplate/js/et2_core_interfaces.js @@ -57,7 +57,23 @@ var et2_IInput = new Interface({ /** * Causes the dirty flag to be reseted. */ - resetDirty: function() {} + resetDirty: function() {}, + + /** + * Checks the data to see if it is valid, as far as the client side can tell. + * Return true if it's not possible to tell on the client side, because the server + * will have the chance to validate also. + * + * The messages array is to be populated with everything wrong with the data, + * so don't stop checking after the first problem unless it really makes sense + * to ignore other problems. + * + * @param {String[]} messages List of messages explaining the failure(s). + * messages should be fairly short, and already translated. + * + * @return {boolean} True if the value is valid (enough), false to fail + */ + isValid: function(messages) {} }); /** diff --git a/etemplate/js/et2_extension_customfields.js b/etemplate/js/et2_extension_customfields.js index afc2000595..15cdbc1905 100644 --- a/etemplate/js/et2_extension_customfields.js +++ b/etemplate/js/et2_extension_customfields.js @@ -190,7 +190,7 @@ var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput var attrs = { 'id': id, 'statustext': field.help, - 'required': field.needed, + 'needed': field.needed, 'readonly': this.options.readonly, 'value': this.options.value[this.prefix+field_name] }; @@ -370,6 +370,11 @@ var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput } } }, + + isValid: function() { + // Individual customfields will handle themselves + return true; + }, /** * Adapt provided attributes to match options for widget diff --git a/etemplate/js/et2_widget_button.js b/etemplate/js/et2_widget_button.js index 82442d3afd..b19055071e 100644 --- a/etemplate/js/et2_widget_button.js +++ b/etemplate/js/et2_widget_button.js @@ -248,6 +248,9 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM], // array. return null; }, + isValid: function() { + return true; + }, /** * et2_IDetachedDOM diff --git a/etemplate/js/et2_widget_link.js b/etemplate/js/et2_widget_link.js index b1a622dd71..8e774a1e98 100644 --- a/etemplate/js/et2_widget_link.js +++ b/etemplate/js/et2_widget_link.js @@ -867,7 +867,7 @@ var et2_link = et2_valueWidget.extend([et2_IDetachedDOM], description: "Array with keys app, id, and optionally title", type: "any" }, - "required": { + "needed": { "ignore": true } }, diff --git a/etemplate/js/et2_widget_radiobox.js b/etemplate/js/et2_widget_radiobox.js index ac6efabc60..548a37c829 100644 --- a/etemplate/js/et2_widget_radiobox.js +++ b/etemplate/js/et2_widget_radiobox.js @@ -236,7 +236,7 @@ var et2_radioGroup = et2_valueWidget.extend([et2_IDetachedDOM], "default": {}, "description": "Options for radio buttons. Should be {value: label, ...}" }, - "required": { + "needed": { "name": "Required", "default": false, "type": "boolean", @@ -258,7 +258,7 @@ var et2_radioGroup = et2_valueWidget.extend([et2_IDetachedDOM], this.node = $j(document.createElement("div")) .addClass("et2_vbox") .addClass("et2_box_widget"); - if(this.options.required) + if(this.options.needed) { // This isn't strictly allowed, but it works this.node.attr("required","required"); @@ -298,7 +298,7 @@ var et2_radioGroup = et2_valueWidget.extend([et2_IDetachedDOM], ro_true: this.options.ro_true, ro_false: this.options.ro_false, readonly: this.options.readonly, - required: this.options.required + needed: this.options.needed }; var radio = et2_createWidget("radio", attrs, this); } diff --git a/etemplate/js/et2_widget_selectAccount.js b/etemplate/js/et2_widget_selectAccount.js index b5189a105a..519960b6d4 100644 --- a/etemplate/js/et2_widget_selectAccount.js +++ b/etemplate/js/et2_widget_selectAccount.js @@ -72,7 +72,7 @@ var et2_selectAccount = et2_selectbox.extend( } // If not required, make sure there's an empty label - if(_attrs['rows'] == 1 && !_attrs['empty_label'] && !_attrs['required']) + if(_attrs['rows'] == 1 && !_attrs['empty_label'] && !_attrs['needed']) { _attrs['empty_label'] = 'None'; } diff --git a/etemplate/js/et2_widget_tabs.js b/etemplate/js/et2_widget_tabs.js index 933cedf778..8f543a6ebe 100644 --- a/etemplate/js/et2_widget_tabs.js +++ b/etemplate/js/et2_widget_tabs.js @@ -304,6 +304,9 @@ var et2_tabbox = et2_valueWidget.extend([et2_IInput], resetDirty: function() { this.value = this.selected_index; + }, + isValid: function(messages) { + return true; } }); et2_register_widget(et2_tabbox, ["tabbox"]); diff --git a/etemplate/js/et2_widget_textbox.js b/etemplate/js/et2_widget_textbox.js index 92d36313f7..dac9453a1b 100644 --- a/etemplate/js/et2_widget_textbox.js +++ b/etemplate/js/et2_widget_textbox.js @@ -208,7 +208,7 @@ var et2_textbox_ro = et2_valueWidget.extend([et2_IDetachedDOM], "size": { "ignore": true }, - "required": { + "needed": { "ignore": true } }, diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index aa0d93a080..7c77ed3999 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -88,7 +88,7 @@ function etemplate2(_container, _menuaction) // List of templates (XML) that are known, but not used. Indexed by id. this.templates = {}; - + // Connect to the window resize event $j(window).resize(this, function(e) {e.data.resize();}); } @@ -115,11 +115,10 @@ etemplate2.prototype.clear = function() { if (this.widgetContainer != null) { -// $j(':input',this.DOMContainer).validator().data("validator").destroy(); this.widgetContainer.free(); this.widgetContainer = null; } - + // Remove self from the index for(name in this.templates) { @@ -262,15 +261,6 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback) etemplate2.prototype.submit = function(button) { - // Validator - /*var valid = true; - var inputs = $j(':input',this.DOMContainer).each(function() { - if(typeof $j(this).data("validator") == "undefined") return true; - valid = valid && $j(this).data("validator").checkValidity(); - return true; - }); - if(!valid) return false;*/ - // Get the form values var values = this.getValues(this.widgetContainer);