How to create a custom validator to check password and confirm password mismatch - angular-reactive-forms

I am working on Angular 11 project I want create a custom validator in reactive form. Here is the code
but it gives following errors
if I omit .value from control.get('password').value error is not shown but validator is not working.
How can I solve this problem.

change control.get('password').value to control.value['password']
change control.get('ConfirmPassword').value to control.value['confirmPassword']

You have problem because , object is possibly null. It's necessary create condition when is object password or confirm Password null return null.
So I am sharing my solution . This working for me.
Code is here
this.userForm = new FormGroup(
{
name: new FormControl('', [
Validators.required,
Validators.email,
Validators.minLength(4),
Validators.maxLength(8),
]),
email: new FormControl('', [
Validators.required,
Validators.email,
Validators.pattern("^[a-z0-9._%+-]+#[a-z0-9.-]+\\.[a-z]{2,4}$")
]),
phone: new FormControl('', [
Validators.required,
Validators.pattern("^((\\+91-?)|0)?[0-9]{10}$") // 10 characters
]),
password: new FormControl('', [
Validators.required,
Validators.minLength(8),
// this.passwordStrengthValidator() // another Validator
]),
password2: new FormControl('', Validators.required),
message: new FormControl('', Validators.required),
},
//this.passwordMatch('password', 'password2') // working
{
validators: this.passwordMatch('password', 'password2') // working
}
);
passwordMatch(password: string, confirmPassword: string): ValidatorFn {
return (formGroup: AbstractControl): { [key: string]: any } | null => {
const passwordControl = formGroup.get(password);
const confirmPasswordControl = formGroup.get(confirmPassword);
if (!passwordControl || !confirmPasswordControl) {
return null;
}
if (
confirmPasswordControl.errors &&
!confirmPasswordControl.errors.mustMatch
) {
return null;
}
if (passwordControl.value !== confirmPasswordControl.value) {
confirmPasswordControl.setErrors({ mustMatch: true });
return { mustMatch: true }
} else {
confirmPasswordControl.setErrors(null);
return null;
}
};
}

Related

"The model user-permissions can't be found."

System Information
Strapi Version: 3.6.5
Operating System: MacOS 11.4
Database: SQL
Node Version: 14.17.0
NPM Version: 6.14.13
Hey there,
I wanted to have a function to change the password by passing the old password and a new one. For that, I found this solution from yohanes (https://stackoverflow.com/a/65237275/9476651). Unfortunately, if I want to execute the POST request, I get the error "Error: The model users-permissions can’t be found. It’s coming from this piece of code:
const user = await strapi.query('user', 'users-permissions').findOne({ email: params.identifier });
This is the first out of maximum three times I need to use the users-permissions plugin and I am pretty sure this error will occur at the other usages as well.
Is there anyone who is able to help me?
Have a great day!
Lucas
My full code:
"use strict";
/**
* api/password/controllers/password.js
*/
const { sanitizeEntity } = require("strapi-utils");
const formatError = (error) => [
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
];
module.exports = {
index: async (ctx) => {
// const params = JSON.parse(ctx.request.body);
const params = ctx.request.body;
// The identifier is required
if (!params.identifier) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.email.provide",
message: "Please provide your username or your e-mail.",
})
);
}
// The password is required
if (!params.password) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your password.",
})
);
}
// The new password is required
if (!params.newPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password.",
})
);
}
if (!params.confirmPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password confirmation.",
})
);
}
if (
params.newPassword &&
params.confirmPassword &&
params.newPassword !== params.confirmPassword
) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.matching",
message: "New passwords do not match.",
})
);
} else if (
params.newPassword &&
params.confirmPassword &&
params.newPassword === params.confirmPassword
) {
// Get user based on identifier
const user = await strapi
.query("user", "users-permissions")
.findOne({ email: params.identifier });
// Validate given password against user query result password
const validPassword = await strapi.plugins[
"users-permissions"
].services.user.validatePassword(params.password, user.password);
if (!validPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.invalid",
message: "Identifier or password invalid.",
})
);
} else {
// Generate new hash password
const password = await strapi.plugins[
"users-permissions"
].services.user.hashPassword({
password: params.newPassword,
});
// Update user password
await strapi
.query("users-permissions")
.update({ id: user.id }, { resetPasswordToken: null, password });
// Return new jwt token
ctx.send({
jwt: strapi.plugins["users-permissions"].services.jwt.issue({
id: user.id,
}),
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query("user", "users-permissions").model,
}),
});
}
}
},
};```
This part of the code works perfectly fine.
const user = await strapi.query('user', 'users-permissions').findOne({ email: params.identifier });
The issue is with the other places where users-permissions is used. You need to use "user", "users-permissions" instead of only "users-permissions". I modified the code below so it works now.
"use strict";
/**
* api/password/controllers/password.js
*/
const { sanitizeEntity } = require("strapi-utils");
const formatError = (error) => [
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
];
module.exports = {
index: async (ctx) => {
// const params = JSON.parse(ctx.request.body);
const params = ctx.request.body;
console.log("params is ", params);
// The identifier is required
if (!params.identifier) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.email.provide",
message: "Please provide your username or your e-mail.",
})
);
}
// The password is required
if (!params.password) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your password.",
})
);
}
// The new password is required
if (!params.newPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password.",
})
);
}
if (!params.confirmPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.provide",
message: "Please provide your new password confirmation.",
})
);
}
if (
params.newPassword &&
params.confirmPassword &&
params.newPassword !== params.confirmPassword
) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.password.matching",
message: "New passwords do not match.",
})
);
} else if (
params.newPassword &&
params.confirmPassword &&
params.newPassword === params.confirmPassword
) {
// Get user based on identifier
const user = await strapi
.query("user", "users-permissions")
.findOne({ email: params.identifier });
// Validate given password against user query result password
const validPassword = await strapi.plugins[
("user", "users-permissions")
].services.user.validatePassword(params.password, user.password);
if (!validPassword) {
return ctx.badRequest(
null,
formatError({
id: "Auth.form.error.invalid",
message: "Identifier or password invalid.",
})
);
} else {
// Generate new hash password
const password = await strapi.plugins[
("user", "users-permissions")
].services.user.hashPassword({
password: params.newPassword,
});
// Update user password
await strapi
.query("user", "users-permissions")
.update({ id: user.id }, { resetPasswordToken: null, password });
// Return new jwt token
ctx.send({
jwt: strapi.plugins[("user", "users-permissions")].services.jwt.issue(
{
id: user.id,
}
),
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query("user", "users-permissions").model,
}),
});
}
}
},
};

laravel vue getting info by hidden field

I need to pass logged user id to back-end and I have vuex store so I can get my user info like {{currentUser.id}} the problem is i cannot pass it to back-end it gives me validation error that user_id is required while i have this hidden input in my form
<input type="hidden" name="user_id" :value="currentUser.id">
for normal inputs i have v-model like v-model="project.title" which is not possible to use on hidden fields.
The question here is how can I pass my user_id to back-end?
Code
<script>
import validate from 'validate.js';
export default {
data: function () {
return {
project: {
title: '',
body: '',
attachment: '',
projectclass: '',
deadline: '',
user_id: '',
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
errors: null
}
},
computed: {
currentUser() {
return this.$store.getters.currentUser;
}
},
methods: {
add() {
this.errors = null;
const errors = validate(this.$data.project);
if(errors) {
this.errors = errors;
return;
}
axios.post('/api/projects/new', this.$data.project)
.then((response) => {
this.$router.push('/projects');
});
}
}
}
</script>
This happens because user_id in this.$data.project dosn't get updated.
Instead of having hidden input you can just do
add() {
this.errors = null;
const errors = validate(Object.assign(this.$data.project, {user_id: this.currentUser.id}));
if(errors) {
this.errors = errors;
return;
}
axios.post('/api/projects/new', Object.assign(this.$data.project, {user_id: this.currentUser.id}))
.then((response) => {
this.$router.push('/projects');
});
}

How to implement Custom Async Validator in Angular4

I'm applying Angular 4 in my project and I am having trouble with Custom Validation using HTTP request.
I also tried this: How to implement Custom Async Validator in Angular2.
But it doesn't work in my project.
Here's what I've done so far:
Validation biding:
userName: ['', [Validators.required, Validators.minLength(3), this.validateUserName.bind(this)]]
Value changes event:
let userNameControl = this.individualForm.get('userName');
userNameControl.valueChanges.subscribe(value => {
this.setErrorMessagesForUserNameControl(userNameControl)
}
);
Validation function:
validateUserName(control: FormControl): Promise<any> {
let promise = new Promise<any>(
(resolve, reject) => {
if (this.oldUserNameForCheck != undefined) {
this._individualUpdateService.isUserNameExisting(this.oldUserNameForCheck, control.value).subscribe(
(res) => {
if (res.isUserNameExisting) {
console.log("existing");
resolve({'existing': true});
} else {
console.log("NOT existing");
resolve(null);
}
},
(error) => {
console.log(error);
}
);
} else {
resolve(null);
}
}
);
return promise;
}
Error Messages
I just try to validate the username by sending it to the back-end.
Here's the logs
As you can see, there's a message for "required", "minlength". Those work fine. But the message for Custom validation is not quite clear.
Async validator should be in the next parameter
userName: ['',
[ Validators.required, Validators.minLength(3) ],
[ this.validateUserName.bind(this) ]
]
Also its better to have a factory method with required dependencies that would create validator, instead of using 'bind(this)'
userNameValidator(originalUsername, individualUpdateService) {
return (control: FormControl): Promise<any> => {
return new Promise<any>((resolve, reject) => {
if (originalUsername != undefined) {
individualUpdateService.isUserNameExisting(originalUsername, control.value).subscribe(
(res) => {
if (res.isUserNameExisting) {
console.log("existing");
resolve({ 'existing': true });
} else {
console.log("NOT existing");
resolve(null);
}
},
(error) => {
console.log(error);
}
);
} else {
resolve(null);
}
})
}
}
userName: ['',
[ Validators.required, Validators.minLength(3) ],
[ this.userNameValidator(this.oldUserNameForCheck, this._individualUpdateService) ]
]

Angular2: how to create custom validator for FormGroup?

i'm creating a form with FormBuilder and i want to add a Validator to a formGroup.
Here is my code:
this.myForm = fb.group({
'name': ['', [Validators.maxLength(50), Validators.required]],
'surname': ['', [Validators.maxLength(50), Validators.required]],
'address': fb.group({
'street': ['', Validators.maxLength(300)],
'place': [''],
'postalcode': ['']
}),
'phone': ['', [Validators.maxLength(25), phoneValidator]],
'email': ['', emailValidator]
});
I would like to conditionally add validators to some of the address's formControls on certain conditions.
So I added a validator in the following way:
'address': fb.group({
'street': ['', Validators.maxLength(300)],
'place': [''],
'postalcode': ['']
}), { validator: fullAddressValidator })
Then i started to create a validator for the address FormGroup:
export const fullAddressValidator = (control:FormGroup) => {
var street:FormControl = control.controls.street;
var place:FormControl = control.controls.place;
var postalcode:FormControl = control.controls.postalcode;
if (my conditions are ok) {
return null;
} else {
return { valid: false };
}
};
I need to add the following conditions:
If all fields are empty the form is valid
If one of the field are filled in then all the fields must be required
If place is instance of country (instead of city) the postalcode
is optional
If the postalcode is filled in then the zipValidator must be
added to its formControl
So, it is possible to add Angular2 Validators to a FormGroup on certain conditions?
If it does, how to implement my conditions? Can i use setValidators() and updateValueAndValidity() in the source code of another validator?
Create a function that takes a parameter and returns a validator function
export const fullAddressValidator = (condition) => (control:FormGroup) => {
var street:FormControl = control.controls.street;
var place:FormControl = control.controls.place;
var postalcode:FormControl = control.controls.postalcode;
if (my conditions are ok) {
return null;
} else {
return { valid: false };
}
};
and use it like
'address': fb.group({
'street': ['', Validators.maxLength(300)],
'place': [''],
'postalcode': ['']
}), { validator: () => fullAddressValidator(condition) })
Yes, it's possible to set FormControl validators inside a FormGroup custom validator. Here is the solution to my needs:
export const fullAddressValidator = (control:FormGroup):any => {
var street:FormControl = control.controls.street;
var place:FormControl = control.controls.place;
var postalcode:FormControl = control.controls.postalcode;
if (!street.value && !place.value && !postalcode.value) {
street.setValidators(null);
street.updateValueAndValidity({onlySelf: true});
place.setValidators(null);
place.updateValueAndValidity({onlySelf: true});
postalcode.setValidators(null);
postalcode.updateValueAndValidity({onlySelf: true});
return null;
} else {
street.setValidators([Validators.required, Validators.maxLength(300)]);
street.updateValueAndValidity({onlySelf: true});
place.setValidators([Validators.required]);
place.updateValueAndValidity({onlySelf: true});
if (place.value instanceof Country) {
postalcode.setValidators(Validators.maxLength(5));
postalcode.updateValueAndValidity({onlySelf: true});
} else {
postalcode.setValidators([zipValidator()]);
postalcode.updateValueAndValidity({onlySelf: true});
}
}
if (street.invalid || place.invalid || postalcode.invalid) {
return {valid: false};
} else {
return null;
}
};

Angular 2 Custom Validator with Observable Parameter

I have this custom validator:
export const mealTypesValidator = (mealSelected: boolean) => {
return (control: FormControl) => {
var mealTypes = control.value;
if (mealTypes) {
if (mealTypes.length < 1 && mealSelected) {
return {
mealTypesValid: { valid: false }
};
}
}
return null;
};
};
If I use it like this it works:
ngOnInit() {
this.findForm = this.formBuilder.group({
categories: [null, Validators.required],
mealTypes: [[], mealTypesValidator(true)],
distanceNumber: null,
distanceUnit: 'kilometers',
keywords: null,
});
}
The catch is, mealSelected is a property on my component - that changes when the user selects and deselects a meal.
How I call the validator above is using static true which can never change.
How can I get the validator to work when I use the component.mealSelected value as the parameter eg:
ngOnInit() {
this.findForm = this.formBuilder.group({
categories: [null, Validators.required],
mealTypes: [[], mealTypesValidator(this.mealSelected)],
distanceNumber: null,
distanceUnit: 'kilometers',
keywords: null,
});
}
Because if i do it as above, it evaluates this.mealSelected instantly which is false at the time - and then when the user selects a meal, it doesn't then go ahead and pass true into the custom validator.
Solution was to move the validator inside my component and use this.mealSelected to check against. Then I had an issue with the validator not being triggered when a meal was selected/deselected and I used this.findForm.controls['mealTypes'].updateValueAndValidity(); to trigger the validation.
Code (can probably be refactored to remove the parameter from the custom validator):
ngOnInit() {
this.findForm = this.formBuilder.group({
categories: [null, Validators.required],
mealTypes: [[], this.mealTypesValidator(true)],
distanceNumber: null,
distanceUnit: 'kilometers',
keywords: null,
});
}
mealTypesValidator = (mealSelected: boolean) => {
return (control: FormControl) => {
var mealTypes = control.value;
if (mealTypes) {
if (mealTypes.length < 1 && this.mealSelected) {
return {
mealTypesValid: { valid: false }
};
}
}
return null;
};
};
However It would still be nice to be able to have a seperate validation module to centralise validation, so if anyone knows how to have a changing parameter value such as a component field as a parameter to a custom validator - like I initially asked, then I'd appreciate an answer that goes with that technique.

Resources