I am trying to implement a custom validator for a paper-input. In this particular case, the control should accept positive numbers. However, not only only will the control only accept positive numbers, it will also run some other custom validation logic to determine if the entry falls within a constantly changing (dynamic & calculated) upper and lower limit. Ideally, the paper-input control's error-message text will also change depending on what part of the custom validator check failed.
In the past, I was able to implement this sort of thing with the gold-email-input element. In that case, the control checks for an entry that matches a regular expression for email addresses (i.e. implements a type-check). It also calls a backend api to see if the email address entered (as it is being typed), already exists in a database. If it exists in the database, the control fails validation and updates the control's validation error-message with a custom message. If it does not exist, it passes validation. As you might have imagined by this description, this was for a user registration UI element whereby the provided email should not already exist in the current list of user accounts. Here is an excerpt of that working code below for your reference:
<gold-email-input id="userEmail" label="Email" required auto-validate value="{{userEmail}}" error-message$="{{_getEmailErrorMsg(0)}}" invalid="{{_emailInvalid}}" validator="_validateEmail"></gold-email-input>
<iron-signals on-iron-signal-email-used="_accountFound" on-iron-signal-email-available="_accountNotFound"></iron-signals>
<script>
var emailErrors = ["Provide a valid email address", "Address already used"];
// Register the polymer element
Polymer({
properties: {
userEmail: {type: String, value: null},
validated: {type: Boolean, notify: true}, //overall validity state of entire element
_emailInvalid: {type: Boolean, value: true, observer: "_validityChanged"}, // validity state of email input itself
},
ready: function() {
// Called before attached
this.$.userEmail.validate = this._validateEmail.bind(this);
},
_accountFound: function() {
// Listener function intended to fire when the user email address/account was found
console.log(this.nodeName + " accountFound listener called\n");
this.$.userEmail.errorMessage = this._getEmailErrorMsg(1);
this._emailInvalid = true;
},
_accountNotFound: function() {
// Listener function intended to fire when the user email address/account was not found
console.log(this.nodeName + " accountNotFound listener called\n");
this.$.userEmail.errorMessage = this._getEmailErrorMsg(0);
this._emailInvalid = false;
},
_checkAccountExistance: function() {
if (this.userEmail !== undefined && this.userEmail != null) {
this.$.user.checkEmailAvailability(this.userEmail);
} else {
this._emailInvalid = true;
}
},
_getEmailErrorMsg: function(code) {
if (code !== undefined && code != null) {
return emailErrors[code];
} else {
return "";
}
},
_validateEmail: function() {
// Custom validator function for email input (also checks if email has already been associated to any user accounts)
console.log(this.nodeName + " validateEmail validator called\n");
// Check if proper email address format (W3C Spec Regex used)
var validEntry = /^[a-zA-Z0-9.!#$%&�*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(this.userEmail);
if (validEntry) {
this._emailInvalid = false;
this._checkAccountExistance();
} else {
this._emailInvalid = true;
}
}
_validityChanged: function(newVal, oldVal) {
// set the containing/parent element's overall validity state
this.validated = (!this._nameInvalid && !this._pwInvalid && !this._phoneInvalid && !this._emailInvalid && !this._countryInvalid && !this._regionInvalid && !this._cityInvalid);
},
});
</script>
Now, if I try to implement a similar approach with the paper-input component, it does not work. The custom validator function does not get called at any point. Is there something inherently different with paper-input compared to gold-email-input? Should it not treat validation the same way?
<paper-input id="xpos" label="Horizontal Position" required auto-validate value="{{XPos}}" error-message="Provide the x position" invalid="{{_xInvalid}}" validator="_validatePosition"></paper-input>
<script>
// Register the polymer element
Polymer({
properties: {
xPos: {type: Number},
validated: {type: Boolean, notify: true}, //overall validity state of entire element
_xInvalid: {type: Boolean, value: true, observer: "_validityChanged"}, // validity state of xpos input itself
},
ready: function() {
// Called before attached
this.$.xpos.validate = this._validatePosition.bind(this);
},
_validatePosition: function() {
console.log(this.nodeName + " validatePosition validator called\n");
// perform some validation code here like the gold-email-input example above
}
});
</script>
Related
I'm building conditional form with vue and stuck on how conditionally validate input field based on the previous users choice. I'll appreciate any help.
in the form I have dropdown menu with payment choices (terminal, payconiq, cash etc).
Second input field is the link user need to add. If user choose terminal, he should add to link input an IP Address. If not - url address.
I receive a paymentId from options with #click, send it to the store and get it from the store with computed property.
The problem is that my vuelidate does not read the condition
const typefromStore = computed(() => paymentStore.paymentType) // here I get typeId from store
const validations = {
description: { required },
type: { required },
link: {
required,
type: typefromStore.value === 1? ipAddress : url // always checks for url and give an error if I need to input IP address
},
}
I read documentation, but I didn't find how to check second input field based on the value of the previous. Only cases when previous field is invalid. But data from dropdown list is always valid.
I need to use the value of 'type' somehow to check conditionally value of link.
Found the solution. May be will help someone. Needed to put validation in computed. No store needed
let newMethod = reactive({
instanceId: 1,
description: '',
type: -1,
link: '',
active: true,
})
const rules = computed(() => {
const localRules = {
description: { required },
type: { required },
link: {},
}
if (newMethod.type === 1) {
localRules.link = {
ipAddress,
}
}
else {
localRules.link = {
url,
}
}
return localRules
})
const v$ = useVuelidate(rules, newMethod, { $autoDirty: true })
I have implemented a custom validation function using the example referenced in the SimpleSchema documentation for validating the uniqueness of a username. In the example, an asynchronous call is made and a custom validation message is displayed if the username is found to already exist.
There is a note, that indicates that if all of the form fields are valid, the form will be submitted, however user creation will fail due to the "unique: true" requirement specified in the schema. Here is the relevant portion of the code from the example docs:
username: {
type: String,
regEx: /^[a-z0-9A-Z_]{3,15}$/,
unique: true,
custom: function () {
if (Meteor.isClient && this.isSet) {
Meteor.call("accountsIsUsernameAvailable", this.value, function (error, result) {
if (!result) {
Meteor.users.simpleSchema().namedContext("createUserForm").addInvalidKeys([{name: "username", type: "notUnique"}]);
}
});
}
}
}
In my case, I have the code working where I am testing if an activation code is valid, I even get the interface to display the error, however since there is no other "schema" failure, the form submits, despite the invalid response... do I need to manually prevent form submission (i.e. using jQuery), or is there something in SimpleSchema I should use instead?
activationCode: {
type: String,
label: "Activation Code",
max: 200,
min: 10,
regEx: /^(?=.*[A-Z])(?=.*\d).+$/,
custom: function() {
if (Meteor.isClient && this.isSet) {
Meteor.call("validateActivationCode", this.value, function(error, result) {
if (result && !result.isValid) {
Clients.simpleSchema().namedContext("signupForm").addInvalidKeys([{
name: "activationCode",
type: "notValid"
}]);
return false;
}
});
}
}
}
Thank You
I have a knockout pureComputed observable that is supposed to return if a account number is valid.
First it checks if the field is empty. (returns false)
Then it checks to see if the account number has changed from what was loaded. (returns true)
Finally it will run the ajax call to get the account info.
If successful it will set the ErrorMessage from the object and the Customer info. Then it is supposed to return true or false based on the ErrorMessage.
If unsuccessful it will set the ErrorMessage and return false.
self.isAccountValid = ko.computed(function () {
if (!self.account()) {//If not complete mark as InValid
self.accountError("Account Number Incomplete.")
return false;
} else if (vm.account == self.account()) {
self.accountError("");
return true;
} else {
var deferred = $.Deferred();
return $.ajax({
url: '/Order/NumberValidation',
data: { account: self.account() }
}).success(function (data) {
self.accountError(data.ErrorMessage);
//Set customer info properties
self.setCustomerInfo(data);
//Set success or warn icon based on error message field
var isValid = !data.ErrorMessage;
deferred.resolve(isValid);
return deferred;
}).error(function (data) {
//Set error message for invalid account
self.accountError("Server error. Please try again in a few minutes.");
//Set warning icon
return false;
})
}
}).extend({ async: ko.observable() });
They extend is a method I found to allow the computed to wait on the ajax call:
ko.extenders.async = function (nonObservableComputed, observableResult) {
nonObservableComputed.subscribe(function (result) {
jQuery.when(result).then(observableResult);
});
return observableResult;
}
This is setting the other data correctly on success, but the return of !data.ErrorMessage which evaluates to false if looking at it through debug is not what is set for the value of isAccountValid. Instead it is being set to the whole data object even though I am trying to return just the boolean.
myViewModel.isAccountValid()
Object {FirstName: null, LastName: null, StreetAddress: null, City: null, State: null…}
City: null
ErrorMessage: "Sequence contains no matching element"
FirstName: null
LastName: null
PhoneNumber: 0
State: null
StreetAddress: null
ZipCode: 0
One problem is that you're using a pureComputed for a function with side effects.
You also have an extra c in acccountError in your success section.
The extender is pretty horrible. It becomes trivial if you pass it the computed you want to use instead of its type, and don't need the added inProgress member.
ko.extenders.async = function (nonObservableComputed, observableResult) {
nonObservableComputed.subscribe(function (result) {
jQuery.when(result).then(observableResult);
});
return observableResult;
}
Demo: http://jsfiddle.net/sp160tan/1/
Update
Pretty sure your problem is that you're returning the ajax result rather than the deferred. Also, the success and error callbacks don't need to return anything; their return values are not used. This could be a source of confusion.
} else {
var deferred = $.Deferred();
$.ajax({
url: '/Order/NumberValidation',
data: { account: self.account() }
}).success(function (data) {
self.accountError(data.ErrorMessage);
//Set customer info properties
self.setCustomerInfo(data);
//Set success or warn icon based on error message field
var isValid = !data.ErrorMessage;
deferred.resolve(isValid);
}).error(function (data) {
//Set error message for invalid account
self.accountError("Server error. Please try again in a few minutes.");
//Set warning icon
deferred.resolve(false);
})
return deferred;
}
I'm using mongoose and trying to set a custom validation that tells the property shall be required (ie. not empty) if another property value is set to something. I'm using the code below:
thing: {
type: String,
validate: [
function validator(val) {
return this.type === 'other' && val === '';
}, '{PATH} is required'
]}
If I save a model with {"type":"other", "thing":""} it fails correctly.
If I save a model with {"type":"other", "thing": undefined} or {"type":"other", "thing": null} or {"type":"other"} the validate function is never executed, and "invalid" data is written to the DB.
As of mongoose 3.9.1, you can pass a function to the required parameter in the schema definition. That resolves this problem.
See also the conversation at mongoose: https://github.com/Automattic/mongoose/issues/941
For whatever reason, the Mongoose designers decided that custom validations should not be considered if the value for a field is null, making conditional required validations inconvenient. The easiest way I found to get around this was to use a highly unique default value that I consider to be "like null".
var LIKE_NULL = '13d2aeca-54e8-4d37-9127-6459331ed76d';
var conditionalRequire = {
validator: function (value) {
return this.type === 'other' && val === LIKE_NULL;
},
msg: 'Some message',
};
var Model = mongoose.Schema({
type: { type: String },
someField: { type: String, default: LIKE_NULL, validate: conditionalRequire },
});
// Under no condition should the "like null" value actually get persisted
Model.pre("save", function (next) {
if (this.someField == LIKE_NULL) this.someField = null;
next()
});
A complete hack, but it has worked for me so far.
Try adding this validation to the type attribute, then adjust your validation accordingly. E.g.:
function validator(val) {
val === 'other' && this.thing === '';
}
thing: {
type: String,
required: function()[{
return this.type === 'other';
}, 'YOUR CUSTOM ERROR MSG HERE']
}
I have a form with 4 tabs and I want to be able to validate the current tab before it switches to the new tab when the user clicks on any other tab. This is what I have so far:
$("#tabs").tabs({
beforeActivate: function(event, ui) {
var tab = ui.oldTab.index();
var valid = true;
if (tab == 0 && ($('#txbYPSchool').val() == "" || $('#txbYPSubjectDesc').val() == "")) {
valid = false;
};
if (!valid) {
//alert('not valid');
event.preventDefault();
}
else {
alert('valid');
}
}
});
The above function prevents the user from selecting a different tab if some fields are not filled in. I also have validate function setup as follows:
$("#form1").validate({
rules: {
txbYPSchool: { required: true, nowhitespace: true },
txbYPSubjectDesc: { required: true, nowhitespace: true }
},
messages: {
txbYPSchool: "*This field is mandatory. If not in school, type 'Not in School'",
txbYPSubjectDesc: "*This field is mandatory"
},
ignore: ""
});
How can I get the validation to run and display the error messages when a user tries to change tabs when there are incomplete fields still on the present tab? I want to be able to do this without a button click and only when a user tries to change tabs but I dont know how to incorporate the validation function into the first function.
Am using jquery 1.9.1 and the latest validation plugin.
Thanks
You can possibly use the plugin's .valid() method.
Replace this...
if (!valid) {
with this...
if !($("#form1").valid()) {
Then since the plugin is checking validity, you can get rid of all this...
var valid = true;
if (tab == 0 && ($('#txbYPSchool').val() == "" || $('#txbYPSubjectDesc').val() == "")) {
valid = false;
};