Details

    • Type: Improvement Improvement
    • Status: Resolved
    • Priority: Minor Minor
    • Resolution: Won't Fix
    • Affects Version/s: 4.1.2
    • Fix Version/s: 4.1.6
    • Component/s: JavaScript
    • Labels:
      None
    • Environment:
      Any

      Description

      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)
        Unknown macro: { 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; }

        }

      1. validation.js
        11 kB
        Andrea Chiumenti

        Activity

        Andrea Chiumenti created issue -
        Andrea Chiumenti made changes -
        Field Original Value New Value
        Attachment validation.js [ 12350139 ]
        Jesse Kuhnert made changes -
        Fix Version/s 4.1.2 [ 12312202 ]
        Fix Version/s 4.1.3 [ 12312516 ]
        Jesse Kuhnert made changes -
        Fix Version/s 4.1.3 [ 12312516 ]
        Fix Version/s 4.1.4 [ 12312763 ]
        Jesse Kuhnert made changes -
        Fix Version/s 4.1.6 [ 12312957 ]
        Fix Version/s 4.1.5 [ 12312935 ]
        Hide
        Andreas Andreou added a comment -
        Show
        Andreas Andreou added a comment - Added the code to the wiki - https://issues.apache.org/jira/browse/TAPESTRY-1246
        Andreas Andreou made changes -
        Resolution Won't Fix [ 2 ]
        Status Open [ 1 ] Resolved [ 5 ]
        Mark Thomas made changes -
        Workflow jira [ 12395538 ] Default workflow, editable Closed status [ 12568552 ]
        Mark Thomas made changes -
        Workflow Default workflow, editable Closed status [ 12568552 ] jira [ 12591595 ]

          People

          • Assignee:
            Unassigned
            Reporter:
            Andrea Chiumenti
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development