I'm learning backbone.js and trying to figure out how to use the validation method. But my problem is when I call for validation the validation is triggered but it still saves the invalid data. As seen here player.set('firstName', '', { validate: true }); I set lastName variable to '' and it saves that values, and it shouldn't.
Edit - Updated code so now it works correctly
Backbone model.
var Player = Backbone.Model.extend({
defaults: {
firstName: "Not specified",
lastName: "Not specified",
position: "Not specified"
},
validate: function (attributes) {
// validate first name
if (!attributes.firstName || attributes.firstName === '') {
return 'you must have a first name';
}
// validate last name
if (!attributes.lastName || attributes.lastName === '') {
return 'you must provide a last name.';
}
// validate position
if (!attributes.position || attributes.position === '') {
return 'you must provide a position.';
}
},
initialize: function () {
// checks when firstName has changed
this.on('change:firstName', function () {
console.log("firstName attribute has changed.");
});
// checks when lastName has changed
this.on('change:lastName', function () {
console.log("lastName attribute has changed.");
});
// checks when position has changed
this.on('change:position', function () {
console.log("position attribute has changed.");
});
// check for valid inputs
this.on('error', function (model, error) {
console.log(error);
});
}
});
From within chrome dev tools I do the following code.
var player = new Player;
player.set({ firstName: "peter", lastName: "andrews", position: "outfield" }, { validate: true });
player.toJSON();
firstName attribute has changed. app.js:32
lastName attribute has changed. app.js:36
position attribute has changed. app.js:40
Object {firstName: "peter", lastName: "andrews", position: "outfield"}
player.set('firstName', '', { validate: true });
player.toJSON();
you must have a first name app.js:14
Object {firstName: "peter", lastName: "andrews", position: "outfield"}
From the Backbone docs:
If the attributes are valid, don't return anything from validate; if
they are invalid, return an error of your choosing.
Your validation function is printing the error strings with console.log but not returning anything.
Try changing your validate function to something like:
validate: function (attributes) {
// validate first name
if (!attributes.firstName || attributes.firstName === '') {
return 'you must have a first name';
}
// validate last name
if (!attributes.lastName || attributes.lastName === '') {
return 'you must provide a last name.';
}
// validate position
if (!attributes.position || attributes.position === '') {
return 'you must provide a position.';
}
},
Related
Can anyone explain how to use the updateOne pre-hook, in mongoose (5.9.5)?
I am need to create a normalised 'shadow field' (not sure the right term) to help with certain searches. While I am able to update the shadow field during a save, I am having trouble during update.
The save pre-hook:
personSchema.pre('save', function (next) {
if (this.isModified('name')) {
const name = this.name;
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.shadowName = shadowName.toLowerCase();
} else {;
this.shadowName = name;
}
}
// do stuff
next();
});
Doing the equivalent for updateOne does not seem to work (shadowName stays with the value it was given during the initial save):
personSchema.pre('updateOne', function (next) {
const name = this.name;
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.update({}, { shadowName: shadowName.toLowerCase() });
} else {
this.shadowName = name;
}
// do stuff
next();
});
The schema:
const personSchema = new mongoose.Schema({
resourceId: {
type: String,
required: true,
unique: true,
index: true,
uppercase: true
},
name:{
type: String,
required:true,
index: true
},
// can be used for searches, but don't update directly
shadowName: {
type: String,
index: true
},
});
BTW I can confirm the hook is called, but the field is not updated.
Turns out you can't access the field values directly and instead need to leverage the get() and set() methods on the query.
Changing the pre-updateOne hook to be the following works:
personSchema.pre('updateOne', function (next) {
const name = this.get('name');
if (name && name.trim().length > 0) {
const shadowName = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
this.set('shadowName', shadowName.toLowerCase());
} else {
this.set('shadowName', name);
}
// do stuff
next();
});
Below, I have mentioned my JS code. So, Kindly suggest or guide me that there is any better approach to implement the validation on form submit for react project or is it right approach which i have implemented already?
submitUserForm = (e) => {
e.preventDefault();
const { formError } = this.state;
let valid = true;
if(document.getElementById('useremail').value === "") {
valid = false;
formError.email = "Kindly enter your email id"
}
else {
valid = true;
formError.email = ""
}
if(document.getElementById('userpassword').value === "") {
valid = false;
formError.password = "Kindly enter the password"
}
else {
valid = true;
formError.password = ""
}
if(valid === true) {
this.formSubmitApi();
}
this.setState({
isValid: valid,
formError
})
}
This is how you can improve your code:
submitUserForm = (e) => {
e.preventDefault();
const formError = {}
if(document.getElementById('useremail').value === "") {
formError.email = "Kindly enter your email id"
}
if(document.getElementById('userpassword').value === "") {
formError.password = "Kindly enter the password"
}
Object.keys(formError).length === 0 && this.formSubmitApi();
this.setState({
formError
})
}
First of all, in order to improve your code, you need to have controlled components (Inputs in this case) in order to store your values in the state and then use them in the submitUserForm function, also, instead of valid variable, I'll use a validation function like (Also, make the useremail and userpassword objects, in order to store the errors there):
state = {
useremail: { value: '', error: '' },
userpassword: { value: '', error: '' },
};
validateValue = value => {
return value !== undefined && value !== null && value !== '';
};
submitUserForm = e => {
const { useremail, userpassword } = this.state;
e.preventDefault();
// If both the 'useremail' and the 'userpassword' pass the 'validateValue' validations, then send the data
if (this.validateValue(useremail.value) && this.validateValue(userpassword.value)) {
this.formSubmitApi(useremail.value, userpassword.value);
// After sending the info, we reset our two states to initial state
this.setState({useremail: { value: '', error: '' }, userpassword: { value: '', error: '' } });
} else {
// If 'useremail' don't pass the validation we store the error
if (!this.validateValue(useremail.value)) {
this.setState({ useremail: { value: useremail.value, error: 'Please enter a valid email' }});
}
// If 'userpassword' don't pass the validation we store the error
if (!this.validateValue(userpassword.value)) {
this.setState({ userpassword: { value: userpassword.value, error: 'Please enter a valid password' }});
}
}
};
I think this is a more clean approach, you only have to states instead of three as before and you can get all the information that you need from those states.
I would change 2 things:
Make the inputs controlled, so that you have better and direct control of the input. Accessing the inputs with document.getElementById is bad practice. Save the text of each input within the state or use refs.
I would change the check for valid input into something like this:
const errors = {};
errors.email = "Kindly enter your email id"
if (Object.keys(errors).length === 0) {...}
It should be a new object, so that you don't mutate the state and this makes it easier because you don't have a second variable.
I hope this helps. Happy coding.
Developing a validation function in React. I fairly new and don't want to develop any bad habits while I'm learning so I'm looking for suggestions on how to clean up a block of code i have here.
The function checks input fields and if they're blank, it attaches the appropriate message to an array. Once all input fields are checked, if that array is empty then proceed to submitting form. If that array contains error messages then the message is displayed to screen (not shown)
validation = (event) => {
event.preventDefault();
let errors = []
const {firstName, lastName, emailAddress, password, confirmPassword} = {...this.state}
if(firstName.length === 0) errors.push('Please provide a value for First Name');
if(lastName.length === 0) errors.push('Please provide a value for Last Name');
if(password.length === 0){
errors.push('Please provide a value for Password');
}else if(password !== confirmPassword){
errors.push('Passwords do not match');
};
if(emailAddress.length === 0) {
errors.push('Please provide a value for Email Address');
}else if(!emailRegex.test(emailAddress)){
errors.push('Invalid email address');
};
this.setState({errors: errors})
if(errors.length === 0) this.logIn()
}
logIn = () => {
console.log('good to go')
};
Just looking for ways to clean up my conditional statements, if possible! Thanks
Perhaps something like the below would suffice. You could simplify this greatly if you provided a generic error message such as "Missing required value: <keyName>", as opposed to something specific for the field.
You'll also want to do a manual check to ensure password === confirmPassword, but I think you'll be able to work that piece out.
const emailRegex = <your regex>;
const hasLength = val => val && val.length !== 0;
Validation Map
const validators = {
firstName: {
validate: hasLength,
message: 'Please provide a value for First Name'
},
lastName: {
validate: hasLength,
message: 'Please provide a value for Last Name'
},
password: {
validate: hasLength,
message: 'Please provide a value for Password'
},
emailAddress: [{
validate: hasLength,
message: 'Please provide a value for Email Address'
},
{
validate: val => !emailRegex.test(val),
message: 'Invalid email address'
}
],
}
Validator
validation = (event) => {
event.preventDefault();
let errors = []
const state = {...this.state};
Object
.keys(state)
.forEach(key => {
let validator = validators[key];
if (!validator) return;
if (!Array.isArray(validator)) {
validator = [validator]
}
validator.forEach(v => {
if (!v.validate(state[key])) {
errors.push(v.message)
}
})
});
this.setState({errors: errors})
if (errors.length === 0) this.logIn()
}
In model:
validation: {
title: {
required: true
},
body: {
required: true
}
}
In view I call:
this.parent.model.isValid(['title', 'body']);
This only return my true/false, how to change validation to get parameters names which are not valid ?
I can't pass atributtes one by one, because It can be a lot.
It's explained in docs mate
Failed validations trigger an "invalid" event, and set the validationError property on the model with the value returned by this method.
var Chapter = Backbone.Model.extend({
validate: function(attrs, options) {
if (attrs.end < attrs.start) {
return "can't end before it starts";
}
}
});
var one = new Chapter({
title : "Chapter One: The Beginning"
});
one.on("invalid", function(model, error) {
alert(model.get("title") + " " + error);
});
In your case (code not tested, I hope you get the idea):
this.parent.model. = Backbone.Model.extend({
validate: function(attrs, options) {
var errors= new Array;
if (!attrs.title) {
errors.push("Title is required");
}
if (!attrs.body) {
errors.push("Body is required");
}
if errors.length
return errors;
}
});
this.parent.model.on("invalid", function(model, error) {
alert(error);
});
//You don't need to pass an attribute list
this.parent.model.isValid();
Note you'll keep the errors array (if any) at this.parent.model.validationError for later processing so you don't need to capture the "invalid" event on the model
Overwrite the validate function http://backbonejs.org/#Model-validate
perform wtv filter you want and return wtv object you want.
Hope it helps
I have a two phone fields on my form - phone and phone2. Only one of them is required. I have been unable to get the validation to work no matter what I have tried. I have tried callbacks and custom validators and it seems like I just can't get it to work. The way I would like it to work is check each field and if both are empty then display a custom message under each. If either one, or both, has data then go ahead and validate each for correctness. If/when the user clicks submit again, check both fields again and if either or both has status message, remove the message start validation on both all over again. I just cant' seem to get this to work and I don't know if it is as easy as somehow forcing the plugin to revalidate all (or custom selected) fields every time the submit button is hit or what.
Hopefully this makes sense. If not please ask for more detail. I've been struggling with this for a while now and have cotton head.
Basically, one of the two fields needs to have data and that data needs to be valid.
I've seen the post on stackoverflow here: Conditional validation with BootstrapValidator but it looks like the answer was for a different plugin.
Here is what I have been trying:
.bootstrapValidator({
live: 'disabled',
message: 'This value is not valid',
fields: {
phone: {
group: '.col-md-3',
validators: {
callback: {
callback: function(value, validator, $field) {
field_name = $($field).attr('name');
if (value.length == 0 && $('#phone2').val() == '') {
return {
valid: false,
message: 'at least one phone number is required'
}
}
else
{
if ( $('#phone2') == '' )
{
$('#defaultForm')
.bootstrapValidator('resetField', $('#phone2') )
//.bootstrapValidator('updateStatus', $('#phone2'), 'NOT_VALIDATED')
;
}
}
value = value.replace(/\D/g, '');
if ( value != '' ) {
if ( !((/^(?:(1\-?)|(\+1 ?))?\(?(\d{3})[\)\-\.]?(\d{3})[\-\.]?(\d{4})$/).test(value) && (value.length == 10)) ) {
//alert("PHONE DID NOT PASS VALIDATION");
return {
valid: false,
message: 'Phone number is not valid'
}
}
else
{
return true;
}
}
else
{
$('#defaultForm')
.bootstrapValidator('resetField', field_name )
.bootstrapValidator('updateStatus', field_name, 'NOT_VALIDATED')
;
return true;
}
}
}
}
},
phone2: {
group: '.col-md-3',
validators: {
callback: {
callback: function(value, validator, $field) {
field_name = $($field).attr('name');
if (value.length == 0 && $('#phone').val() == '') {
return {
valid: false,
message: 'at least one phone number is required'
}
}
else
{
if ( $('#phone') == '' )
{
$('#defaultForm')
.bootstrapValidator('resetField', $('#phone') )
//.bootstrapValidator('updateStatus', $('#phone'), 'NOT_VALIDATED')
;
}
}
value = value.replace(/\D/g, '');
if ( value != '' ) {
if ( !((/^(?:(1\-?)|(\+1 ?))?\(?(\d{3})[\)\-\.]?(\d{3})[\-\.]?(\d{4})$/).test(value) && (value.length == 10)) ) {
//alert("PHONE2 DID NOT PASS VALIDATION");
return {
valid: false,
message: 'Phone number2 is not valid'
}
}
else
{
return true;
}
}
else
{
$('#defaultForm')
.bootstrapValidator('resetField', field_name )
.bootstrapValidator('updateStatus', field_name, 'NOT_VALIDATED')
;
return true;
}
}
}
}
}
}
})
A little late but...
You have to validate both phone values in both callback functions. When one of the phones is valid, set the other as valid.
fields: {
phone: {
validators: {
callback: {
callback: function (value: any, validator: any, field: any) {
// Get the value of the field 'phone2'
const phone2 = validator.getFieldElements('phone2').val();
// Neither phone and phone2 are valid?
if (!value && !phone2) {
return false;
}
// phone is valid. Update validity of phone2.
validator.updateStatus('phone2', validator.STATUS_VALID);
return true;
},
message: 'at least one phone number is required'
}
}
},
phone2: {
validators: {
callback: {
callback: function (value: any, validator: any, field: any) {
// Get the value of the field 'phone'
const phone = validator.getFieldElements('phone').val();
// Neither phone2 and phone are valid?
if (!value && !phone) {
return false;
}
// phone2 is valid. Update validity of phone.
validator.updateStatus('phone', validator.STATUS_VALID);
return true;
},
message: 'at least one phone number is required'
}
}
}
}