【问题标题】:Dojo validation of a textarea文本区域的 Dojo 验证
【发布时间】:2011-12-25 17:01:30
【问题描述】:

我是第一次尝试使用 dojo,所以这可能很明显。

我有一个非常简单的表格,里面有一个需要填写的文本区域。

<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js"></script>
<form id='form1' action="" method="" dojoType="dijit.form.Form">
  <label for="dob">desc:</label>
  <textarea class='multilinecontrol' dojoType="dijit.form.Textarea" selected='true' required='true'></textarea>
  <button type='submit' id="next" name="next" dojoType="dijit.form.Button">
    Next</button>
</form>

我添加了“必需”属性,因此我可以确保表单在用户继续之前有效。

但是,当表单显示时,textarea 周围有一个红色焦点环,其他小部件都没有这样的行为,真的很烦人。

知道如何摆脱它吗?

我可以通过在“把东西放在这里”等一些默认文本来破解它,但是我必须做额外的验证工作——我目前不知道该怎么做。

【问题讨论】:

    标签: validation textarea dojo required


    【解决方案1】:

    不用复制整个现有的类,使用 mixin 就足够了:

    define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/SimpleTextarea", "dijit/form/ValidationTextBox"],
    function(declare, lang, SimpleTextarea, ValidationTextBox) {
    
      return declare('dijit.form.ValidationTextArea', [SimpleTextarea, ValidationTextBox], {
        constructor: function(params){
          this.constraints = {};
          this.baseClass += ' dijitValidationTextArea';
        },    
        templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-       point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
        validator: function(value, constraints) {
          return (new RegExp("^(?:" + this._computeRegexp(constraints) + ")"+(this.required?"":"?")+"$",["m"])).test(value) &&
            (!this.required || !this._isEmpty(value)) &&
            (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
        }
        })
      })
    

    不幸的是,我无法通过红色感叹号获得与验证输入完全相同的行为 - 因为 Textarea 可以调整大小,所以我完成了 CSS 技巧:

    .dijitValidationTextAreaError, .dijitValidationTextAreaError.dijitTextBoxHover {
     background-image: url("error.png");
     background-position: right;
     background-repeat: no-repeat;
    }
    

    error.png 需要从 claro 主题复制到您的 css 位置。它显示在文本区域内部,而不是外部,但它是唯一的、非常小的区别。

    【讨论】:

    • 你有没有找到一种方法来获取典型的错误行为(文本区域下的红色文本)而不是悬停工具提示?
    【解决方案2】:

    根据您的问题,我猜您使用的是 dojo 1.6

    这是我的ValidationTextarea。它使用 SimpleTextArea 来支持 'row' 和 'cols' 以及 ValidationTextBox 进行验证。

    这个想法类似于另一个答案,只是在这种情况下,我没有为小部件定义 templateString

    dojo.provide('mynamespace.ValidationTextarea');
    dojo.require("dijit.form.ValidationTextBox");
    dojo.require("dijit.form.SimpleTextarea");
    
    //ValidationTextarea: Validation class that hide the displayMessage and 
    //implement the validate method used by forms
    dojo.declare("mynamespace.ValidationTextarea",[dijit.form.ValidationTextBox,dijit.form.SimpleTextarea], {
    
    //int
    // The maximum number of characters
    maxLength: 255,
    
    validate: function() {
    if (arguments.length==0)
            return this.validate(false);
    
        return this.inherited(arguments);
    },            
    
    onFocus: function() {
        if (!this.isValid()) {
            this.displayMessage(this.getErrorMessage());
        }
    },
    
    onBlur: function() {
        this.validate(false);
    },
    
    filter: function(/*String*/ value){
        // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
        // as \r\n instead of just \n
        if(value){
            value = value.replace(/\r/g,"");
        }
        return this.inherited(arguments);
    },
    
    displayMessage: function(/*String*/ message) { 
    
    }
    
    }); 
    

    【讨论】:

      【解决方案3】:

      dijit.form.Textarea 不进行任何验证。这是针对面临相同问题的任何人的自定义 ValidationTextArea:

      dojo.provide("dijit.form.ValidationTextArea");
      
      dojo.require("dojo.i18n");
      
      dojo.require("dijit.form.TextBox");
      dojo.require("dijit.Tooltip");
      
      dojo.requireLocalization("dijit.form", "validate");
      
      /*=====
          dijit.form.ValidationTextBox.__Constraints = function(){
              // locale: String
              //        locale used for validation, picks up value from this widget's lang attribute
              // _flags_: anything
              //        various flags passed to regExpGen function
              this.locale = "";
              this._flags_ = "";
          }
      =====*/
      
      dojo.declare(
          "dijit.form.ValidationTextArea",
          dijit.form.TextBox,
          {
              // summary:
              //        Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
              // tags:
              //        protected
      
              templateString: "<textarea name=${name} ${nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
              baseClass: "dijitTextArea",
      
              attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
                  rows:"textbox", cols: "textbox"
              }),
      
              // rows: Number
              //        The number of rows of text.
              rows: "3",
      
              // rows: Number
              //        The number of characters per line.
              cols: "20",
      
              // required: Boolean
              //        User is required to enter data into this field.
              required: false,
      
              // promptMessage: String
              //        If defined, display this hint string immediately on focus to the textbox, if empty.
              //        Think of this like a tooltip that tells the user what to do, not an error message
              //        that tells the user what they've done wrong.
              //
              //        Message disappears when user starts typing.
              promptMessage: "",
      
              // invalidMessage: String
              //         The message to display if value is invalid.
              invalidMessage: "$_unset_$", // read from the message file if not overridden
      
              // constraints: dijit.form.ValidationTextBox.__Constraints
              //        user-defined object needed to pass parameters to the validator functions
              constraints: {},
      
              // regExp: [extension protected] String
              //        regular expression string used to validate the input
              //        Do not specify both regExp and regExpGen
              regExp: "(.|[\r\n])*",
      
              regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){
                  // summary:
                  //        Overridable function used to generate regExp when dependent on constraints.
                  //        Do not specify both regExp and regExpGen.
                  // tags:
                  //        extension protected
                  return this.regExp; // String
              },
      
              // state: [readonly] String
              //        Shows current state (ie, validation result) of input (Normal, Warning, or Error)
              state: "",
      
              // tooltipPosition: String[]
              //        See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
              tooltipPosition: [],
      
              _setValueAttr: function(){
                  // summary:
                  //        Hook so attr('value', ...) works.
                  this.inherited(arguments);
                  this.validate(this._focused);
              },
      
              validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
                  // summary:
                  //        Overridable function used to validate the text input against the regular expression.
                  // tags:
                  //        protected
                  return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
                      (!this.required || !this._isEmpty(value)) &&
                      (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
              },
      
              _isValidSubset: function(){
                  // summary:
                  //        Returns true if the value is either already valid or could be made valid by appending characters.
                  //        This is used for validation while the user [may be] still typing.
                  return this.textbox.value.search(this._partialre) == 0;
              },
      
              isValid: function(/*Boolean*/ isFocused){
                  // summary:
                  //        Tests if value is valid.
                  //        Can override with your own routine in a subclass.
                  // tags:
                  //        protected
                  return this.validator(this.textbox.value, this.constraints);
              },
      
              _isEmpty: function(value){
                  // summary:
                  //        Checks for whitespace
                  return /^\s*$/.test(value); // Boolean
              },
      
              getErrorMessage: function(/*Boolean*/ isFocused){
                  // summary:
                  //        Return an error message to show if appropriate
                  // tags:
                  //        protected
                  return this.invalidMessage; // String
              },
      
              getPromptMessage: function(/*Boolean*/ isFocused){
                  // summary:
                  //        Return a hint message to show when widget is first focused
                  // tags:
                  //        protected
                  return this.promptMessage; // String
              },
      
              _maskValidSubsetError: true,
              validate: function(/*Boolean*/ isFocused){
                  // summary:
                  //        Called by oninit, onblur, and onkeypress.
                  // description:
                  //        Show missing or invalid messages if appropriate, and highlight textbox field.
                  // tags:
                  //        protected
                  var message = "";
                  var isValid = this.disabled || this.isValid(isFocused);
                  if(isValid){ this._maskValidSubsetError = true; }
                  var isValidSubset = !isValid && isFocused && this._isValidSubset();
                  var isEmpty = this._isEmpty(this.textbox.value);
                  if(isEmpty){ this._maskValidSubsetError = true; }
                  this.state = (isValid || (!this._hasBeenBlurred && isEmpty) || isValidSubset) ? "" : "Error";
                  if(this.state == "Error"){ this._maskValidSubsetError = false; }
                  this._setStateClass();
                  dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
                  if(isFocused){
                      if(isEmpty){
                          message = this.getPromptMessage(true);
                      }
                      if(!message && (this.state == "Error" || (isValidSubset && !this._maskValidSubsetError))){
                          message = this.getErrorMessage(true);
                      }
                  }
                  this.displayMessage(message);
                  return isValid;
              },
      
              // _message: String
              //        Currently displayed message
              _message: "",
      
              displayMessage: function(/*String*/ message){
                  // summary:
                  //        Overridable method to display validation errors/hints.
                  //        By default uses a tooltip.
                  // tags:
                  //        extension
                  if(this._message == message){ return; }
                  this._message = message;
                  dijit.hideTooltip(this.domNode);
                  if(message){
                      dijit.showTooltip(message, this.domNode, this.tooltipPosition);
                  }
              },
      
              _refreshState: function(){
                  // Overrides TextBox._refreshState()
                  this.validate(this._focused);
                  this.inherited(arguments);
              },
      
              //////////// INITIALIZATION METHODS ///////////////////////////////////////
      
              constructor: function(){
                  this.constraints = {};
              },
      
              postMixInProperties: function(){
                  // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
                  if(!this.value && this.srcNodeRef){
                      this.value = this.srcNodeRef.value;
                  }
                  this.inherited(arguments);
                  this.constraints.locale = this.lang;
                  this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
                  if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
                  var p = this.regExpGen(this.constraints);
                  this.regExp = p;
                  var partialre = "";
                  // parse the regexp and produce a new regexp that matches valid subsets
                  // if the regexp is .* then there's no use in matching subsets since everything is valid
                  if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
                      function (re){
                          switch(re.charAt(0)){
                              case '{':
                              case '+':
                              case '?':
                              case '*':
                              case '^':
                              case '$':
                              case '|':
                              case '(':
                                  partialre += re;
                                  break;
                              case ")":
                                  partialre += "|$)";
                                  break;
                               default:
                                  partialre += "(?:"+re+"|$)";
                                  break;
                          }
                      }
                  );}
                  try{ // this is needed for now since the above regexp parsing needs more test verification
                      "".search(partialre);
                  }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
                      partialre = this.regExp;
                      console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
                  } // should never be here unless the original RE is bad or the parsing is bad
                  this._partialre = "^(?:" + partialre + ")$";
              },
      
              filter: function(/*String*/ value){
                  // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
                  // as \r\n instead of just \n
                  if(value){
                      value = value.replace(/\r/g,"");
                  }
                  return this.inherited(arguments);
              },
      
              _setDisabledAttr: function(/*Boolean*/ value){
                  this.inherited(arguments);    // call FormValueWidget._setDisabledAttr()
                  this._refreshState();
              },
      
              _setRequiredAttr: function(/*Boolean*/ value){
                  this.required = value;
                  dijit.setWaiState(this.focusNode,"required", value);
                  this._refreshState();
              },
      
              postCreate: function(){
                  if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
                      var s = dojo.getComputedStyle(this.focusNode);
                      if(s){
                          var ff = s.fontFamily;
                          if(ff){
                              this.focusNode.style.fontFamily = ff;
                          }
                      }
                  }
                  this.inherited(arguments);
                  if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
                      dojo.addClass(this.textbox, "dijitTextAreaCols");
                  }
              },
      
              reset:function(){
                  // Overrides dijit.form.TextBox.reset() by also
                  // hiding errors about partial matches
                  this._maskValidSubsetError = true;
                  this.inherited(arguments);
              },
      
              _onBlur: function(){
                  this.displayMessage('');
                  this.inherited(arguments);
              },
      
              _previousValue: "",
              _onInput: function(/*Event?*/ e){
                  // Override TextBox._onInput() to enforce maxLength restriction
                  if(this.maxLength){
                      var maxLength = parseInt(this.maxLength);
                      var value = this.textbox.value.replace(/\r/g,'');
                      var overflow = value.length - maxLength;
                      if(overflow > 0){
                          if(e){ dojo.stopEvent(e); }
                          var textarea = this.textbox;
                          if(textarea.selectionStart){
                              var pos = textarea.selectionStart;
                              var cr = 0;
                              if(dojo.isOpera){
                                  cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
                              }
                              this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
                              textarea.setSelectionRange(pos-overflow, pos-overflow);
                          }else if(dojo.doc.selection){ //IE
                              textarea.focus();
                              var range = dojo.doc.selection.createRange();
                              // delete overflow characters
                              range.moveStart("character", -overflow);
                              range.text = '';
                              // show cursor
                              range.select();
                          }
                      }
                      this._previousValue = this.textbox.value;
                  }
                  this.inherited(arguments);
              }
          }
      );
      

      【讨论】:

        【解决方案4】:

        我的方法与前两个答案略有不同,但希望更简洁,因为它不涉及编写任何验证代码。

        开箱即用 dijit/form/SimpleTextarea 通过将控件替换为

        来扩展 TextBox

        我的解决方案只是通过扩展 ValidationTextBox 来定义一个新的文本区域 Dijit。

        即从SimpleTextarea(90行,大部分是cmets)中获取源码,将TextBox替换为ValidationTextBox。同时覆盖默认的正则表达式模式 (.*) 以允许换行符。

        基于 SimpleTextarea.js.uncompressed.js

        define("custom/ValidationTextarea", [
        "dojo/_base/declare", // declare
        "dojo/dom-class", // domClass.add
        "dojo/sniff", // has("ie") has("opera")
        "dijit/form/ValidationTextBox"
        ], function(declare, domClass, has, ValidationTextBox){
        
        return declare("custom/ValidationTextarea", ValidationTextBox, {        
        
            baseClass: "dijitValidationTextBox dijitTextBox dijitTextArea",
        
        ...
            pattern: "[\\S\\s]+", /* Match not-whitepsace or whitespace. Default pattern for ValidationTextBox is .* which does not match new line characters */
        
        ... remaining lines
        

        【讨论】:

          【解决方案5】:

          这是我的 SimpleValidationTextArea。基本上我已经合并了 dijit/form/ValidationTextBox 和 dijit/form/TextArea。这段代码很丑陋,需要一些改进,但它可以工作;)

          define("dijit/form/SimpleValidationTextArea", [
              "dojo/_base/declare", // declare
              "dojo/_base/kernel", // kernel.deprecated
              "dojo/i18n", // i18n.getLocalization
              "./TextBox",
              "../Tooltip",
              "dojo/text!./templates/ValidationTextBox.html",
              "dojo/dom-class", // domClass.add
              "dojo/sniff", // has("ie") has("opera")
              "dojo/i18n!./nls/validate"
          ], function(declare, kernel, i18n, TextBox, Tooltip, template, domClass, has){
          
          //To use this widget you need to add this line to your css file
          //errorBorder{ border-color: #D46464; }
          
          var SimpleValidationTextArea;
          return SimpleValidationTextArea = declare("dijit.form.SimpleValidationTextArea", TextBox, {
              /* from SimpleTextarea */
          
              // summary:
              //      A simple textarea that degrades, and responds to
              //      minimal LayoutContainer usage, and works with dijit/form/Form.
              //      Doesn't automatically size according to input, like Textarea.
              //
              // example:
              //  |   <textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
              //
              // example:
              //  |   new SimpleTextarea({ rows:20, cols:30 }, "foo");
          
              baseClass: "dijitValidationTextBox dijitTextArea",
          
              // rows: Number
              //      The number of rows of text.
              rows: "3",
          
              // rows: Number
              //      The number of characters per line.
              cols: "20",
          
              templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
          
              buildRendering: function(){
                  this.inherited(arguments);
                  if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
                      domClass.add(this.textbox, "dijitTextAreaCols");
                  }
              },
          
              filter: function(/*String*/ value){
                  // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
                  // as \r\n instead of just \n
                  if(value){
                      value = value.replace(/\r/g,"");
                  }
                  return this.inherited(arguments);
              },
          
              _onInput: function(/*Event?*/ e){
                  // Override TextBox._onInput() to enforce maxLength restriction
                  if(this.maxLength){
                      var maxLength = parseInt(this.maxLength);
                      var value = this.textbox.value.replace(/\r/g,'');
                      var overflow = value.length - maxLength;
                      if(overflow > 0){
                          var textarea = this.textbox;
                          if(textarea.selectionStart){
                              var pos = textarea.selectionStart;
                              var cr = 0;
                              if(has("opera")){
                                  cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
                              }
                              this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
                              textarea.setSelectionRange(pos-overflow, pos-overflow);
                          }else if(this.ownerDocument.selection){ //IE
                              textarea.focus();
                              var range = this.ownerDocument.selection.createRange();
                              // delete overflow characters
                              range.moveStart("character", -overflow);
                              range.text = '';
                              // show cursor
                              range.select();
                          }
                      }
                  }
                  this.inherited(arguments);
              },
          
              /* -from SimpleTextarea */
          
              // summary:
              //      Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
          
              // required: Boolean
              //      User is required to enter data into this field.
              required: false,
          
              // promptMessage: String
              //      If defined, display this hint string immediately on focus to the textbox, if empty.
              //      Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
              //      Think of this like a tooltip that tells the user what to do, not an error message
              //      that tells the user what they've done wrong.
              //
              //      Message disappears when user starts typing.
              promptMessage: "",
          
              // invalidMessage: String
              //      The message to display if value is invalid.
              //      The translated string value is read from the message file by default.
              //      Set to "" to use the promptMessage instead.
              invalidMessage: "$_unset_$",
          
              // missingMessage: String
              //      The message to display if value is empty and the field is required.
              //      The translated string value is read from the message file by default.
              //      Set to "" to use the invalidMessage instead.
              missingMessage: "$_unset_$",
          
              // message: String
              //      Currently error/prompt message.
              //      When using the default tooltip implementation, this will only be
              //      displayed when the field is focused.
              message: "",
          
              // constraints: __Constraints
              //      user-defined object needed to pass parameters to the validator functions
              constraints: {},
          
              // pattern: [extension protected] String|Function(constraints) returning a string.
              //      This defines the regular expression used to validate the input.
              //      Do not add leading ^ or $ characters since the widget adds these.
              //      A function may be used to generate a valid pattern when dependent on constraints or other runtime factors.
              //      set('pattern', String|Function).
              pattern: ".*",
          
              // regExp: Deprecated [extension protected] String.  Use "pattern" instead.
              regExp: "",
          
              regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){
                  // summary:
                  //      Deprecated.  Use set('pattern', Function) instead.
              },
          
              // state: [readonly] String
              //      Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
              state: "",
          
              // tooltipPosition: String[]
              //      See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
              tooltipPosition: [],
          
              _deprecateRegExp: function(attr, value){
                  if(value != SimpleValidationTextArea.prototype[attr]){
                      kernel.deprecated("SimpleValidationTextArea id="+this.id+", set('" + attr + "', ...) is deprecated.  Use set('pattern', ...) instead.", "", "2.0");
                      this.set('pattern', value);
                  }
              },
              _setRegExpGenAttr: function(/*Function*/ newFcn){
                  this._deprecateRegExp("regExpGen", newFcn);
                  this.regExpGen = this._getPatternAttr; // backward compat with this.regExpGen(this.constraints)
              },
              _setRegExpAttr: function(/*String*/ value){
                  this._deprecateRegExp("regExp", value);
              },
          
              _setValueAttr: function(){
                  // summary:
                  //      Hook so set('value', ...) works.
                  this.inherited(arguments);
                  this.validate(this.focused);
              },
          
              validator: function(/*anything*/ value, /*__Constraints*/ constraints){
                  // summary:
                  //      Overridable function used to validate the text input against the regular expression.
                  // tags:
                  //      protected
                  return (new RegExp("^(?:" + this._getPatternAttr(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
                      (!this.required || !this._isEmpty(value)) &&
                      (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
              },
          
              _isValidSubset: function(){
                  // summary:
                  //      Returns true if the value is either already valid or could be made valid by appending characters.
                  //      This is used for validation while the user [may be] still typing.
                  return this.textbox.value.search(this._partialre) == 0;
              },
          
              isValid: function(/*Boolean*/ /*===== isFocused =====*/){
                  // summary:
                  //      Tests if value is valid.
                  //      Can override with your own routine in a subclass.
                  // tags:
                  //      protected
                  return this.validator(this.textbox.value, this.constraints);
              },
          
              _isEmpty: function(value){
                  // summary:
                  //      Checks for whitespace
                  return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
              },
          
              getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
                  // summary:
                  //      Return an error message to show if appropriate
                  // tags:
                  //      protected
                  var invalid = this.invalidMessage == "$_unset_$" ? this.messages.invalidMessage :
                      !this.invalidMessage ? this.promptMessage : this.invalidMessage;
                  var missing = this.missingMessage == "$_unset_$" ? this.messages.missingMessage :
                      !this.missingMessage ? invalid : this.missingMessage;
                  return (this.required && this._isEmpty(this.textbox.value)) ? missing : invalid; // String
              },
          
              getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
                  // summary:
                  //      Return a hint message to show when widget is first focused
                  // tags:
                  //      protected
                  return this.promptMessage; // String
              },
          
              _maskValidSubsetError: true,
              validate: function(/*Boolean*/ isFocused){
                  // summary:
                  //      Called by oninit, onblur, and onkeypress.
                  // description:
                  //      Show missing or invalid messages if appropriate, and highlight textbox field.
                  // tags:
                  //      protected
                  var message = "";
                  var isValid = this.disabled || this.isValid(isFocused);
                  if(isValid){ this._maskValidSubsetError = true; }
                  var isEmpty = this._isEmpty(this.textbox.value);
                  var isValidSubset = !isValid && isFocused && this._isValidSubset();
                  this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && (this._maskValidSubsetError || (isValidSubset && !this._hasBeenBlurred && isFocused))) ? "Incomplete" : "Error"));
                  this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
          
                  if(this.state == "Error"){
                      this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
                      message = this.getErrorMessage(isFocused);
                      domClass.add(this.domNode, "errorBorder");
                  }else if(this.state == "Incomplete"){
                      message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
                      this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
                  }else if(isEmpty){
                      message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
                  }else if(this.state == ''){ //everything is fine
                      domClass.remove(this.domNode, "errorBorder");
                  }
                  this.set("message", message);
          
                  return isValid;
              },
          
              displayMessage: function(/*String*/ message){
                  // summary:
                  //      Overridable method to display validation errors/hints.
                  //      By default uses a tooltip.
                  // tags:
                  //      extension
                  if(message && this.focused){
                      Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
                  }else{
                      Tooltip.hide(this.domNode);
                  }
              },
          
              _refreshState: function(){
                  // Overrides TextBox._refreshState()
                  if(this._created){
                      this.validate(this.focused);
                  }
                  this.inherited(arguments);
              },
          
              //////////// INITIALIZATION METHODS ///////////////////////////////////////
          
              constructor: function(params /*===== , srcNodeRef =====*/){
                  // summary:
                  //      Create the widget.
                  // params: Object|null
                  //      Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
                  //      and functions, typically callbacks like onClick.
                  //      The hash can contain any of the widget's properties, excluding read-only properties.
                  // srcNodeRef: DOMNode|String?
                  //      If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
          
                  this.constraints = {};
                  this.baseClass += ' dijitSimpleValidationTextArea';
              },
          
              startup: function(){
                  this.inherited(arguments);
                  this._refreshState(); // after all _set* methods have run
              },
          
              _setConstraintsAttr: function(/*__Constraints*/ constraints){
                  if(!constraints.locale && this.lang){
                      constraints.locale = this.lang;
                  }
                  this._set("constraints", constraints);
                  this._refreshState();
              },
          
              _setPatternAttr: function(/*String|Function*/ pattern){
                  this._set("pattern", pattern); // don't set on INPUT to avoid native HTML5 validation
              },
          
              _getPatternAttr: function(/*__Constraints*/ constraints){
                  // summary:
                  //      Hook to get the current regExp and to compute the partial validation RE.
                  var p = this.pattern;
                  var type = (typeof p).toLowerCase();
                  if(type == "function"){
                      p = this.pattern(constraints || this.constraints);
                  }
                  if(p != this._lastRegExp){
                      var partialre = "";
                      this._lastRegExp = p;
                      // parse the regexp and produce a new regexp that matches valid subsets
                      // if the regexp is .* then there's no use in matching subsets since everything is valid
                      if(p != ".*"){
                          p.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
                          function(re){
                              switch(re.charAt(0)){
                                  case '{':
                                  case '+':
                                  case '?':
                                  case '*':
                                  case '^':
                                  case '$':
                                  case '|':
                                  case '(':
                                      partialre += re;
                                      break;
                                  case ")":
                                      partialre += "|$)";
                                      break;
                                   default:
                                      partialre += "(?:"+re+"|$)";
                                      break;
                              }
                          });
                      }
                      try{ // this is needed for now since the above regexp parsing needs more test verification
                          "".search(partialre);
                      }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
                          partialre = this.pattern;
                          console.warn('RegExp error in ' + this.declaredClass + ': ' + this.pattern);
                      } // should never be here unless the original RE is bad or the parsing is bad
                      this._partialre = "^(?:" + partialre + ")$";
                  }
                  return p;
              },
          
              postMixInProperties: function(){
                  if(!this.value && this.srcNodeRef){
                      this.value = this.srcNodeRef.value;
                  }
                  this.inherited(arguments);
                  this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
                  this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
              },
          
              _setDisabledAttr: function(/*Boolean*/ value){
                  this.inherited(arguments);  // call FormValueWidget._setDisabledAttr()
                  this._refreshState();
              },
          
              _setRequiredAttr: function(/*Boolean*/ value){
                  this._set("required", value);
                  this.focusNode.setAttribute("aria-required", value);
                  this._refreshState();
              },
          
              _setMessageAttr: function(/*String*/ message){
                  this._set("message", message);
                  this.displayMessage(message);
              },
          
              reset:function(){
                  // Overrides dijit/form/TextBox.reset() by also
                  // hiding errors about partial matches
                  this._maskValidSubsetError = true;
                  this.inherited(arguments);
              },
          
              _onBlur: function(){
                  // the message still exists but for back-compat, and to erase the tooltip
                  // (if the message is being displayed as a tooltip), call displayMessage('')
                  this.displayMessage('');
          
                  this.inherited(arguments);
              }
          });
          });
          

          【讨论】:

            猜你喜欢
            • 2011-07-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-03-25
            • 2012-09-12
            相关资源
            最近更新 更多