You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by "Andreas Andreou (JIRA)" <de...@tapestry.apache.org> on 2008/02/27 18:09:57 UTC

[jira] Resolved: (TAPESTRY-1246) Clientside validation patch

     [ https://issues.apache.org/jira/browse/TAPESTRY-1246?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Andreas Andreou resolved TAPESTRY-1246.
---------------------------------------

    Resolution: Won't Fix

Added the code to the wiki - https://issues.apache.org/jira/browse/TAPESTRY-1246

> Clientside validation patch
> ---------------------------
>
>                 Key: TAPESTRY-1246
>                 URL: https://issues.apache.org/jira/browse/TAPESTRY-1246
>             Project: Tapestry
>          Issue Type: Improvement
>          Components: JavaScript
>    Affects Versions: 4.1.2
>         Environment: Any
>            Reporter: Andrea Chiumenti
>            Priority: Minor
>             Fix For: 4.1.6
>
>         Attachments: validation.js
>
>
> I've patched the client side validation script file so that an input field in exception status reports its related exceptions with a dojo tooltip.
> When the validation is performed a second time all the exception  tooltip widgets are disposed and recreated as necessary.
> I hope you find it useful.
> Ciao,
> kiuma
> vlidation.js
> ===============================================================================================
> dojo.provide("tapestry.form.validation");
> dojo.require("dojo.validate.check");
> dojo.require("dojo.html.style");
> dojo.require("dojo.widget.*");
> dojo.require("tapestry.widget.AlertDialog");
> dojo.require("dojo.widget.Tooltip");
> dojo.require("dojo.collections.ArrayList");
> tapestry.form.validation={
> 	//exceptionWidgets: [], //new dojo.collections.ArrayList(), 
> 	missingClass:"fieldMissing", // default css class that will be applied to fields missing a value
> 	invalidClass:"fieldInvalid", // default css class applied to fields with invalid data
> 	
> 	dialogName:"tapestry:AlertDialog",
> 	
> 	/**
> 	 * Main entry point for running form validation. The
> 	 * props object passed in contains a number of fields that 
> 	 * are managed by tapestry.form:
> 	 * 
> 	 * 		props = {
> 	 * 			validateForm:[true|false] // whether to run validation at all
> 	 * 			profiles:[profile1, profile2] // set of dojo.validate.check() style profiles
> 	 * 										  // that may have been registered with form
> 	 * 		}
> 	 * 
> 	 * The individual profiles will contain any of the data described by the dojo documentation
> 	 * for dojo.validate.check(). In addition to that, each profile will also have a corresponding
> 	 * string message to display if the specified condition has been met. For example, if you have
> 	 * specified that a select field named "select1" was required your profile would look something
> 	 * like:
> 	 * 
> 	 * 		profile = {
> 	 * 			"required":["select1"], // normal dojo.validate.check data
> 	 * 			"select1":{ // tapestry field/error type specific data
> 	 * 				"required":"You must select a value for select1."
> 	 * 			}
> 	 * 		}
> 	 * 
> 	 * It is intended for you to call dojo.validate.check(form, profile) for each profile
> 	 * stored in the "profiles" field, as well as deciding how to display errors / warnings.
> 	 * 
> 	 * @return Boolean indicating if form submission should continue. If false the form
> 	 * 			will ~not~ be submitted. 
> 	 */
> 	validateForm:function(form, props){
> 		if (typeof form == "undefined") {return false;}
> 		if (typeof props == "undefined") {return true;} // form exists but no profile? just submit I guess..
> 		if (!props.validateForm) {return true;}
> 		
> 		try {
> 			this.clearValidationDecorations(form, props);			
> 			for (var i=0; i < props.profiles.length; i++) {
> 				this._clearExceptionWidgets(props.profiles[i]);
> 				var results=dojo.validate.check(form, props.profiles[i]);				
> 				if (!this.processResults(form, results, props.profiles[i])) {
> 					this.summarizeErrors(form, results, props.profiles[i]);
> 					return false;
> 				}
> 			}
> 		} catch (e) { 
> 			// since so many dynamic function calls may happen in here it's best that we 
> 			// catch all of them and log them or else peoples forms might still get submitted
> 			// and they'd never be able to figure out what was wrong
> 			dojo.log.exception("Error validating", e, true);
> 			return false;
> 		}
> 		
> 		return true;
> 	},
> 	
> 	/**
> 	 * Called for each registered profile on a form after 
> 	 * dojo.validate.check() has been called. This function is 
> 	 * expected to do UI related notifications of fields in error.
> 	 * 
> 	 * @param form The form that was validated.
> 	 * @param results The result of calling dojo.validate.check(form,profile)
> 	 * @param profile The original profile used to validate form, also holds 
> 	 * 				  validation error messages to be used for each field.
> 	 * 
> 	 * @return Boolean, if false form should not be submitted and all validation 
> 	 * 		   should be stopped. If true validation will continue and eventually
> 	 * 		   form will be submitted.
> 	 */
> 	processResults:function(form, results, profile){
> 		if (results.isSuccessful()) { return true; } 
> 		
> 		var formValid=true;
> 		if (results.hasMissing()) {
> 			var missing=results.getMissing();
> 			for (var i=0; i < missing.length; i++) {
> 				this.handleMissingField(missing[i], profile);
> 			}
> 			
> 			formValid=false;
> 		}
> 		
> 		if (results.hasInvalid()) {
> 			var invalid=results.getInvalid();
> 			for (var i=0; i < invalid.length; i++) {
> 				this.handleInvalidField(invalid[i], profile);
> 			}
> 			
> 			formValid=false;
> 		}
> 		
> 		return formValid; // if got past successful everything is invalid
> 	},
> 	
> 	/**
> 	 * Default field decorator for missing fields.
> 	 * 
> 	 * @param field The field element that was missing data.
> 	 * @param profile The form validation profile.
> 	 */
> 	handleMissingField:function(field, profile){
> 		field=dojo.byId(field);
> 		if (dj_undef("type", field)) {return;}
> 		dojo.html.removeClass(field, this.invalidClass);
> 		
> 		if (!dojo.html.hasClass(field, this.missingClass)){
> 			dojo.html.prependClass(field, this.missingClass);
> 		}
> 	},
> 	
> 	/**
> 	 * Default field decorator for invalid fields.
> 	 * 
> 	 * @param field The field element that had invalid data.
> 	 * @param profile The form validation profile.
> 	 */
> 	handleInvalidField:function(field, profile){
> 		field=dojo.byId(field);
> 		if (dj_undef("type", field)) {return;}
> 		dojo.html.removeClass(field, this.missingClass);
> 		
> 		if (!dojo.html.hasClass(field, this.invalidClass)){
> 			dojo.html.prependClass(field, this.invalidClass);
> 		}
> 	},
> 	
> 	/**
> 	 * Clears out previous css classes set on fields 
> 	 * in error.
> 	 */
> 	clearValidationDecorations:function(form, props){
> 		for (var i=0; i< form.elements.length; i++) {
> 			if (dj_undef("type", form.elements[i]) || typeof form.elements[i].type == "undefined"
> 				|| form.elements[i].type == "submit" 
> 				|| form.elements[i].type == "hidden") { continue; }
> 			
> 			dojo.html.removeClass(form.elements[i], this.missingClass);
> 			dojo.html.removeClass(form.elements[i], this.invalidClass);
> 		}
> 	},
> 	
> 	/**
> 	 * Optionally allows an alert dialog/dhtml dialog/etc to 
> 	 * be displayed to user to alert them to the invalid state
> 	 * of their form if validation errors have occurred. 
> 	 * 
> 	 * @param form The form being validated.
> 	 * @param results Returned value of dojo.validate.check(form, profile)
> 	 * @param profile Validation profile definition 
> 	 */
> 	summarizeErrors:function(form, results, profile){
> 		var merrs=[];
> 		var ierrs=[];
> 		var fieldErrs=[];
> 		
> 		tapestry.form.currentFocus=null;
> 		//this._clearExceptionWidgets();
> 		if (results.hasMissing()){
> 			var fields=results.getMissing();
> 			for (var i=0; i<fields.length; i++){
> 				fieldErrs=[];
> 				if(i==0 && !tapestry.form.currentFocus){
> 					tapestry.form.currentFocus=fields[i];
> 				}
> 				
> 				if (profile[fields[i]] && profile[fields[i]]["required"]){
> 					if (dojo.lang.isArray(profile[fields[i]]["required"])) {					
> 						for (var z=0; z < profile[fields[i]]["required"].length; z++) {
> 							merrs.push(profile[fields[i]]["required"][z]);
> 							fieldErrs.push(profile[fields[i]]["required"][z]);
> 						}
> 					} else {
> 						merrs.push(profile[fields[i]]["required"]);
> 						fieldErrs.push(profile[fields[i]]["required"]);
> 					}
> 					//alert(fields[i]);
> 					this._buildExceptionTooltipWidget(profile, fields[i], fieldErrs, null);
> 				}				
> 			}
> 		}
> 		if (results.hasInvalid()){
> 			var fields=results.getInvalid();
> 			for (var i=0; i<fields.length; i++){
> 				fieldErrs=[];
> 				if(i==0 && !tapestry.form.currentFocus){
> 					tapestry.form.currentFocus=fields[i];
> 				}
> 				if (profile[fields[i]] && profile[fields[i]]["constraints"]){
> 					if (dojo.lang.isArray(profile[fields[i]]["constraints"])) {
> 						for (var z=0; z < profile[fields[i]]["constraints"].length; z++) {
> 							ierrs.push(profile[fields[i]]["constraints"][z]);
> 							fieldErrs.push(profile[fields[i]]["constraints"][z]);
> 						}
> 					} else {
> 						ierrs.push(profile[fields[i]]["constraints"]);
> 						fieldErrs.push(profile[fields[i]]["constraints"]);
> 					}
> 					//alert(fields[i]);
> 					this._buildExceptionTooltipWidget(profile, fields[i], null, fieldErrs);
> 				}
> 			}
> 		}
> 		
> 		var msg="";
> 		if (merrs.length > 0) {
> 			msg+='<ul class="missingList">';
> 			for (var i=0; i<merrs.length;i++) {
> 				msg+="<li>"+merrs[i]+"</li>";
> 			}
> 			msg+="</ul>";
> 		}
> 		if (ierrs.length > 0) {
> 			msg+='<ul class="invalidList">';
> 			for (var i=0; i<ierrs.length;i++) {
> 				msg+="<li>"+ierrs[i]+"</li>";
> 			}
> 			msg+="</ul>";
> 		}
> 		
> 		var ad=dojo.widget.byId("validationDialog");
> 		if (ad) {
> 			ad.setMessage(msg);
> 			ad.show();
> 			return;
> 		}
> 		
> 		var node=document.createElement("span");
> 		document.body.appendChild(node);
> 		var dialog=dojo.widget.createWidget(this.dialogName, 
> 						{
> 							widgetId:"validationDialog",
> 							message:msg
> 						}, node);
> 		dialog.show();
> 	},
> 	
> 	/**
> 	 * Clears all exception tooltip widgets
> 	 * 	 
> 	 */
> 	_clearExceptionWidgets: function(profile) {
> 		if (!profile.exceptionWidgets) {
> 			profile.exceptionWidgets = new dojo.collections.ArrayList();
> 		}
> 		var iter = profile.exceptionWidgets.getIterator();
> 		
> 		while (widget = iter.get()) {
> 			try {
> 				widget.destroy()
> 			} catch (e) {
> 				dojo.log.exception("Error destroying widget.", e, true);
> 			}
> 			
> 		}
> 		profile.exceptionWidgets.clear();
> 	},
> 	/**
> 	 * Creates the field exception tooltip if necessary.
> 	 * 
> 	 * @param fieldId The id of the field that has to expose an exception tooltip
> 	 * @return Returns The exception tooltip dom element.
> 	 */
> 	_createExceptionTooltip: function(fieldId) {		
> 		
> 		var id = fieldId + "-err";
> 		
> 		var el = dojo.byId(id);
> 		if (!el) {
> 			el = document.createElement("div");		
> 			el.id=id;
> 			el.className="exceptionTooltip";			
> 			dojo.dom.insertBefore(el, dojo.byId(fieldId));			
> 		}			
> 		el.innerHTML = "";
> 		return el;		
> 	},
> 	/**
> 	 * Fills the content of the exception tooltip with exception messages
> 	 * if messages is not empty
> 	 *	 
> 	 * @param exceptions The exception message array 
> 	 * @param listType May be 'missingList' or 'invalidList', default is 'missingList'.
> 	 *
> 	 * @return Returns the innerHTML that needs to be added to the tooltip dom element
> 	 */
> 	_buildExceptionTooltip: function(exceptions, listType) {		
> 		var msg ="";
> 		
> 		if ((exceptions)&&(exceptions.length > 0)) {
> 			if (!listType) {
> 				listType = "missingList";
> 			}
> 			msg+='<ul class="'+listType+'">';
> 			for (var i=0; i<exceptions.length;i++) {
> 				msg+="<li>"+exceptions[i]+"</li>";
> 			}
> 			msg+="</ul>";
> 		}
> 		return msg;		
> 	},
> 	/**
> 	 * Creates a tooltip exception widget.
> 	 *
> 	 * @param fieldId. The id of the field in exception status.
> 	 * @param missingListExceptions The array containing missing value exception messages
> 	 * @param invalidListExceptions The array containing invalid value exception messages
> 	 */
> 	_buildExceptionTooltipWidget: function(profile, fieldId, missingListExceptions, invalidListExceptions) {
> 		if (!profile.exceptionWidgets) {
> 			profile.exceptionWidgets = new dojo.collections.ArrayList();
> 		}
> 		var tooltipEl = this._createExceptionTooltip(fieldId);
> 		tooltipEl.innerHTML = this._buildExceptionTooltip(missingListExceptions, "missingList");
> 		tooltipEl.innerHTML += this._buildExceptionTooltip(invalidListExceptions, "invalidList");
> 		var widget = dojo.widget.createWidget("Tooltip", {id:fieldId + "-tip", connectId:fieldId, toggle:"explode"}, tooltipEl);		
> 		profile.exceptionWidgets.add(widget);
> 	},
> 	/**
> 	 * Validates that the input value matches the given 
> 	 * regexp pattern.
> 	 * 
> 	 * @param value The string value to be evaluated.
> 	 * @param pattern The regexp pattern used to match against value.
> 	 */
> 	isValidPattern:function(value, pattern){
> 		if (typeof value != "string" || typeof pattern != "string") { return false; }
> 		
> 		var re = new RegExp(pattern);
> 		return re.test(value);
> 	},
> 	
> 	isPalleteSelected:function(elem){
> 		if (elem.length > 0) { return true; }
> 		return false;
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org