Dojo 验证文本区域

发布于 2024-12-14 13:49:33 字数 812 浏览 3 评论 0原文

我是第一次尝试使用 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>

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

然而,当表单显示时,文本区域周围有一个红色焦点环,其他小部件都没有像这样的行为,这真的很烦人。

知道如何摆脱它吗?

我可以通过放入某种默认文本(例如“将东西放在这里”)来破解它,但随后我必须做额外的验证工作 - 我目前不知道该怎么做。

I'm attempting to use dojo for the first time, so this may be be obvious.

I have a very simple form with one textarea in it that needs to be filled in.

<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>

I've added the 'required' property, so i can ensure the form is valid before the user can proceed.

However when the form is show the textarea has a red focus ring around it, None of the other widgets behave like this and its really annoying.

Any idea how to get rid of it?

I could hack it by putting some kind of default text in like 'Put stuff here' but then I have to do extra validation work - which I presently can't work out how to do.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

十年九夏 2024-12-21 13:49:33

不用复制整个现有的类,使用 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 位置。它显示在文本区域内部,而不是外部,但这是唯一的、很小的区别。

Instead of copying the whole existing classes, it's enough to use 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
    }
    })
  })

Unfortunatelly, I wasn't able to get exactly the same behaviour with red exclamation sign as with validating input - because of Textarea resizing factility, so I've done the CSS trick:

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

The error.png needs to be copied from claro theme to your css location. It is displayed inside the text area, not outside it, but it's the only, quite minor difference.

猛虎独行 2024-12-21 13:49:33

这是我的 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);
    }
});
});

Here is my SimpleValidationTextArea. Basically I've merged dijit/form/ValidationTextBox and dijit/form/TextArea. This code is ugly and needs some improvements but it works ;)

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);
    }
});
});
蓝梦月影 2024-12-21 13:49:33

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

开箱即用的 dijit/form/SimpleTextarea 通过用

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

也就是说,从 SimpleTextarea 获取源代码(90 行和大部分注释),并将 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

My approach is slightly different to the previous two answers but it is hopefully more concise as it does not involve writing any validation code.

The out of the box dijit/form/SimpleTextarea extends a TextBox by replacing the control with a <textarea>

My solution just defines a new text area Dijit by extending a ValidationTextBox.

That is, take the source from SimpleTextarea (90 lines and most of it comments) and replace TextBox with ValidationTextBox. Also override the default regex pattern (.*) to allow new line characters.

Based on 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
浅唱々樱花落 2024-12-21 13:49:33

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);
        }
    }
);

dijit.form.Textarea doesn't do any validation. here is a custom ValidationTextArea for anybody facing the same issue:

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);
        }
    }
);
最好是你 2024-12-21 13:49:33

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

这是我的 ValidationTextarea。它使用 SimpleTextArea 支持“行”和“列”,并使用 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) { 

}

}); 

Based on your question, I guess you are using dojo 1.6

This is my ValidationTextarea. It uses SimpleTextArea to support 'row' and 'cols' and ValidationTextBox for validation stuff.

The idea is similar to another answer just that in this case I don't define a templateString for the widget.

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) { 

}

}); 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文