I am trying to create a custom Dojo ValidationIdBox widget that inherits from ValidationTextBox. My custom widget will add async duplicate ID checking with a back-end server.
I sub-classed dijit.form.ValidationTextBox and modified two methods: isValid() and validate(). However, I can’t get the validation to fully work. The widget catches and highlights problems like missing required input. It even catches when there is a duplicate ID (the tooltip appears), but it fails to highlight the field as expected.
I have tried to isolate my problem with a simplified code snippet below. It is mostly the origininal Dojo code with some minor modifications. My general strategy is to let the widget validate as a regular ValidationTextBox, then test for a duplicate ID. I modified isValid() to have two modes: plain validation and validation with uniqueness check. Currently, the uniqueness test intentionally always fails.
In a similar fashion, I modified validate() to do its normal thing, then do some additional processing if normal validation succeeds but the validation with uniqueness test fails. I tried to mirror the same logic as when the ValidationTextBox is in an error state, but the same effects are not mirrored: The ‘ID not available’ tooltip appears, but the red outline with exclamation mark does not appear.
I examined ValidationTextBox’s code, but I cannot figure out how that special styling is triggered… can someone explain how ValidationTextArea works? Specifically I’m not quite sure how this._maskValidSubsetError, aria-invalid, and this.state are used.
(Also sometimes, I want the tooltip to appear, but not the red styling. Like to show when AJAX duplicate ID check request is processing.)
// If ValidationTextBoxValidates
isValid: function(isFocused, requireUnique) {
if (typeof requireUnique === 'undefined') requireUnique = false;
var isValid = this.inherited(arguments);
var isUnique = false;
return (requireUnique ? (isValid && isUnique) : isValid);
},
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) ? "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);
}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
}
/// Begin custom widget code
if (isValid && !this.isValid(isFocused, true) ) {
isValid = false;
var isValidSubset = !isValid && isFocused && this._isValidSubset();
this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
message = 'ID not available';
this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
}
/// End custom widget code
this.set("message", message);
return isValid;
},
might be missing the obvious, hidden amongst a cloud of inline &&|| ;)))))
The point in the blur/keypress mechanism is that the tooltip will only be visible on the box that is currently shown, hence _maskValid
Have you tried
this.set("state", this.isUnique() ? "" : "Error");??Widgets are Stateful and the .set might just do the trick, firing an event or publish a topic