Custom Validation with parameter - javascript

I am using Ionic 2 with Angular 2 beta 11.
I have a custom validator that works perfectly. However, I now need to add another custom validation method, but can't seem to work out how to pass a parameter to it.
As you can see below, I have a custom validation function:
ValidationService.userNameExists(control, this.employeeService)
'control' does not exist
How do I pass the control (FormControl) to the userNameExists function? Or, I need to pass the field value (value of username).
Thanks
register.ts
constructor(private nav: NavController, private builder: FormBuilder, employeeService: EmployeeService) {
this.employeeService = employeeService;
this.registerForm = builder.group({
'username': ['', [Validators.required, Validators.minLength(5), Validators.maxLength(55), ValidationService.userNameValidator, ValidationService.userNameExists(control, this.employeeService)]],
'password1': ['', [Validators.required, Validators.minLength(5), Validators.maxLength(45), ValidationService.passwordValidator]],
'password2': ['', [Validators.required, Validators.minLength(5), Validators.maxLength(45), ValidationService.passwordValidator]]
}, { 'validator': ValidationService.matchPassword('password1', 'password2') }
);
}
'control' does not exist
ValidationService.ts
public static userNameExists(control: FormControl, employeeService: EmployeeService): any {
return (control: FormControl) => {
let userNameInput = control.value;
console.log('userNameExists: '+control.value);
let promise: Promise<EmployeeModel> = employeeService.getEmployeByUserName(userNameInput);
promise.then((employeeModel: EmployeeModel) => {
if (employeeModel.userName === userNameInput) {
return { userNameExists: true };
} else {
return null;
}
});
}
}

Remember that when you are instantiating FormControl/FormGroup, you are passing the validation function in, not calling it. Change your username declaration as following:
'username': ['',
[Validators.required,
Validators.minLength(5),
Validators.maxLength(55),
ValidationService.userNameValidator,
(control) => ValidationService.userNameExists(control, this.employeeService)]]

Related

Why does using a form input rather than static string prevent my code from generating a qr code?

Trying to pass a text input value from my form to a JS library to process a qr code does not work. Adding a static value works all the time. How do I make it so that the value from my form works. For example making text = "2743987947" or text = text = "hello" works.
Console logging the input value gives me the correct value. However using this value to create a qr code fails.
Here is my code.
export class HomeComponent implements OnInit {
designForm: FormGroup;
printForm: FormGroup;
qrcodeImage;
load: boolean;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.designForm = this.formBuilder.group({
model: ['', Validators.compose([Validators.required])],
color: [
'',
Validators.compose([
Validators.required,
Validators.pattern('[\\w\\-\\s\\/]+'),
]),
],
space: ['', Validators.compose([Validators.required])],
serial1: ['', Validators.compose([Validators.required])],
serial2: ['', Validators.compose([Validators.required])],
});
this.printForm = this.formBuilder.group({
name: [
'',
Validators.compose([
Validators.required,
Validators.pattern('Martin Tecno'),
]),
],
});
this.qrcodeImage = this.createQrcode();
}
createQrcode() {
let text = this.designForm.value.serial1;
let typeNumber = 4;
let errorCorrectionLevel = 'H';
let qr = qrcode(typeNumber, errorCorrectionLevel);
qr.addData(text);
qr.make();
// return qr.createTableTag();
// return qr.createSvgTag();
return qr.createImgTag();
}
onSubmit(designForm) {
console.log(designForm);
console.log(this.qrcodeImage);
console.log(this.designForm.value.serial1);
}
}```

Add/ remove custom validators to form array in angular 8

Hi I am trying to set/ take away custom validators for different elements in a form array that can change around, so far what I have tried to do is create a switch statement and loop through all of the input types that are set so I could set the validation rule as well as send a message to the user if the rule isn't met. The problem I am having is the form is initialized before the form data is set.
So my question is how can I loop through the array and set the validation rules. If someone could let me know if im along the right tracks with using a switch statement but have code in the wrong place or if there is a different and better approach it would be most helpful thank you
export class ReactiveComponent implements OnInit {
public form: FormGroup;
public fieldList: any;
types: Array<any>;
formData: any;
Param: string;
setData: any;
formLabelNames: any;
get contactFormGroup() {
return this.form.get('inputs') as FormArray;
}
constructor(
private route: ActivatedRoute,
private fb: FormBuilder,
private api: FormService,
private notifiy: NotificationService,
private auth: AuthService,
private router: Router) { }
ngOnInit() {
this.form = this.fb.group({
name: ['', Validators.compose([Validators.required])],
organization: ['', Validators.compose([Validators.required])],
inputs: this.fb.array([this.createForm()])
});
this.route.paramMap.subscribe(params => {
this.Param = params.get('id');
this.getForm(this.Param);
});
// set fieldslist to this field
this.fieldList = this.form.get('inputs') as FormArray;
}
// formgroup
createForm(): FormGroup {
return this.fb.group({
type: ['', Validators.compose([Validators.required])],
name: ['', Validators.compose([Validators.required])],
value: ['', this.validators()]
});
}
getForm(id) {
this.api.getForm(id).subscribe(
(data: any) => this.setForm(data)
);
}
getFieldsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
getContactsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
setForm(data) {
const d = data.results;
this.setData = d;
this.formLabelNames = d[0].fields;
this.form.patchValue({
name: [d[0].form_name],
organization: [d[0].org],
});
this.form.setControl('inputs', this.setExistingFields(d[0].fields));
}
setExistingFields(fields: any): FormArray {
const formArray = new FormArray([]);
this.fieldList = formArray;
fields.forEach(f => {
formArray.push(this.fb.group({
name: f.name,
type: f.type,
value: f.value
}));
});
return formArray;
}
/* This is where I have tried to create a switch statement but I get a undefined error because the setform function is being called after this one */
validators() {
this.formLabelNames.type.forEach((field: any) => {
switch (field.type) {
case 'email':
}
});
}
submit() {
if (this.form.valid) {
const formId = this.Param;
const local = this.auth.decodePayload();
const userId = local.sub;
this.router.navigateByUrl('/dashboard');
this.api.sendForm(this.form.value, formId, userId).subscribe();
this.form.reset();
} else {
this.notifiy.showFailure('Form is not valid', 'Error');
}
}
}
You have several problems, as far as I can judge:
You don't initialize neither the formControls nor the formArray correctly inside of you formGroup. It should rather look like this:
this.form = this.fb.group({
name: new FormControl( "", {validators: [Validators.required]}),
organization: new FormControl( "", {validators: [Validators.required]}),
inputs: this.fb.array([new FormControl(), new FormControl()]),
});
Besides: What is the point of using a formArray when it consists of only one formGroup?
Off course you can set the validators for any abstractControl be it a formControl or a formGroup. For the array it would look like something like this:
this.formArray.controls.map((ctrl) => {
ctrl.setValidators([Validators.required, Validators.email]);
});
It doesn't matter where in the component class you put your method. Though it surely matters when that method is invoked!

Form Builder - Cannot read property 'get' of undefined, issue with validators

I have a Form Builder form in my Angular 6 project, and I'm getting an issue with the validators.
I think I know what the issue is, but don't know how to fix it.
I have a custom validator (not really custom, I use min() and max() with a custom variable for the check)
And probably the form is initialized before those values.
The form is declared before the constructor (I tried moving it into the construction and into ngOnInit, same result)
myForm = this.fb.group({
title: [""],
date: [""],
max_score: [, [Validators.required, Validators.max(6), Validators.min(1)]],
min_score: [, [Validators.required, Validators.max(6), Validators.min(1)]]
});
constructor(private fb: FormBuilder) {}
Like this everything works, even the validators.
The max_score and min_score are 2 dropdowns, where you can choose a number.
What I want to achieve is this:
max_score: [, [Validators.required, Validators.max(6), Validators.min(this.myForm.get("min_points").value)]],
min_score: [, [Validators.required, Validators.max(this.myForm.get("max_score").value), Validators.min(1)]]
So basically the max_score cannot be lower than the min_score, and the min_score cannot be higher than the max_score!
But this gives me the error:
TypeError: Cannot read property 'get' of undefined
So I guess that's because this.myForm.get("max_score").value is not accessible when the Form builder is initialized.
How can I solve this? Those values are optional in my form, so it's correct that they have no value at the start, and I just want to put in place a check that avoids that a min_value is chosen that is higher than the max_value
I even tried put the this.myForm.get.... in a function:
getMaxPoints(): number {
if (this.myForm.get("max_score").value) {
return this.myForm.get("max_score").value;
} else return null;
}
and then
min_score: [, [Validators.required, Validators.max(this.getMaxPoints()), Validators.min(1)]]
but I get the same error!
What's the workaround for this issue?
it should be:
public myForm:FormGroup; // you should do this
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
title: [''],
date: [''],
max_score: ['', [Validators.required, Validators.max(6), Validators.min(1)]],
min_score: ['', [Validators.required, Validators.max(6), Validators.min(1)]]
});
}
and access it:
this.myForm.get("max_score").value
or:
this.myForm.controls["max_score"].value
You can add a (keyup)="checkMinMax()" in your html both on min and max. In the checkMinMax function you can check the contents of this.myForm.get("max_score").value and this.myForm.get("min_score").value either show a message with *ngIf like you mentioned in your comment or force the min or max value to be the same with the other if the user's input exceeds it. ( or both ).
Additionally by doing this you can just use the required validator.
When you create a form, you pass values to the validators array, values that are passed only at creation time.
So if you use an expression like
Validators.max(this.myForm.get("max_points").value)
you are evaluating during the creation of the form. So myForm variable is still undefined.
Try using some variables instead of accessing myForm properties like this:
minVal: number = 5;
maxVal: number = 5;
max_score: [, [Validators.required, Validators.max(6), Validators.min(minVal)]],
min_score: [, [Validators.required, Validators.max(maxVal), Validators.min(1)]]
To validate data getting some parameters from the form you should use the extra from this.fb.group()
You can try something like this:
myForm = this.fb.group({
title: [""],
date: [""],
max_score: [, [Validators.required]],
min_score: [, [Validators.required]]
},
{
// THIS IS EXTRA!!!!!
validator: [customValidator()]
});
This is an example of customValidator:
export function customValidator() {
return (group: FormGroup) => {
const minScore = group.controls['min_score'],
maxScore = group.controls['max_core'],
minPoints = group.controls['min_points'];
if (/** some condition **/) {
return minPoints.setErrors({'notMatching': true});
} else {
return minPoints.setErrors(null);
}
};
}
Note that into customValidator you are accessing myForm data dynamically accessing its controls.
Then in your template you need to show errors depending on
<div *ngIf="f.min_points.errors && f.min_points.errors.notMatching">NOT VALID</div>
I don't remember if is available a notMatching error, but you can print all available errors with
<pre>{{ f.min_points.errors | json }</pre>
where f is a convenience getter method like this:
get f() {
return this.myForm.controls;
}
I believe I have simple workaround for this problem. It's a timing issue. I made it work by defining the member variable as static. My code like below:
static numberOfVerses: number;
..
form: FormGroup = new FormGroup({
suraID: new FormControl('', Validators.required),
bVerseID: new FormControl('', Validators.required),
eVerseID: new FormControl('', Validators.required),
translatorID1: new FormControl(''),
},validate);
// reference the static variable in the custom validation
validate(control: AbstractControl): { [key: string]: boolean } | null {
const suraID = control.get('suraID');
const bVerseID = control.get('bVerseID');
const eVerseID = control.get('eVerseID');
const translatorID1 = control.get('translatorID1');
if (suraID && bVerseID && eVerseID && QuranSearchComponent.numberOfVerses) {
if (bVerseID.value > QuranSearchComponent.numberOfVerses || eVerseID.value > QuranSearchComponent.numberOfVerses) {
return { 'invalid_verse_number': true };
}
}
if (suraID && bVerseID && eVerseID && translatorID1 &&
bVerseID.value > eVerseID.value) {
return { 'invalid_verse_range': true };
}
return null;
}

I want to take the data into the form of already signup members?

I have a form,and when i submit the form the form data is submitting to the angular firebase database.so if we sigin again i want to show the data into the form which has been submitted before.
my .ts file is below
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder,Validators } from "#angular/forms";
import { AF } from "app/providers/af";
import { FirebseService } from "app/firebse.service";
import { Router } from "#angular/router";
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2/database';
#Component({
selector: 'app-candidate-registration',
templateUrl: './candidate-registration.component.html',
styleUrls: ['./candidate-registration.component.css']
})
export class CandidateRegistrationComponent implements OnInit {
itemsAsObjects = [{value: 0, display: 'Angular'}, {value: 1, display: 'React'}];
complexForm : FormGroup;
contents:any;
constructor(fb: FormBuilder,
private firebaseService:FirebseService,
private router: Router,
private db: AngularFireDatabase) {
var Userid=localStorage.getItem('user');
console.log(Userid);
let content= this.db.object('/candidates_list/'+Userid)
content.subscribe(data => {
console.log(data);
this.contents=data;
console.log(this.contents);
})
if(this.contents){
this.complexForm = fb.group({
// To add a validator, we must first convert the string value into an array. The first item in the array is the default value if any, then the next item in the array is the validator. Here we are adding a required validator meaning that the firstName attribute must have a value in it.
'firstName' : ["pranav", Validators.required],
// We can use more than one validator per field. If we want to use more than one validator we have to wrap our array of validators with a Validators.compose function. Here we are using a required, minimum length and maximum length validator.
'lastName': ["kk", Validators.compose([Validators.required, Validators.minLength(1), Validators.maxLength(10)])],
'gender' : [null, Validators.required],
'email' : [null, Validators.required],
'contact_number':[null, Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(10)])],
'experience':[null, Validators.required],
'skills':[null, Validators.required],
'notice_period':[null, Validators.required],
})
}else
{
this.complexForm = fb.group({
// To add a validator, we must first convert the string value into an array. The first item in the array is the default value if any, then the next item in the array is the validator. Here we are adding a required validator meaning that the firstName attribute must have a value in it.
'firstName' : [null, Validators.required],
// We can use more than one validator per field. If we want to use more than one validator we have to wrap our array of validators with a Validators.compose function. Here we are using a required, minimum length and maximum length validator.
'lastName': [null, Validators.compose([Validators.required, Validators.minLength(1), Validators.maxLength(10)])],
'gender' : [null, Validators.required],
'email' : [null, Validators.required],
'contact_number':[null, Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(10)])],
'experience':[null, Validators.required],
'skills':[null, Validators.required],
'notice_period':[null, Validators.required],
})
}
}
ngOnInit() {
}
submitForm(user){
console.log(user);
this.firebaseService.addtolist(user);
this.complexForm .reset();
this.router.navigate(['/reg-complete']);
}
}
this below code(part of .ts file) is working fine.i am getting the signed in users data into console.But i dont know how to use it into if condition, to set the data of the already signed in users into the registration form.
anyone please help me?Thanks in advance.
var Userid=localStorage.getItem('user');
console.log(Userid);
let content= this.db.object('/candidates_list/'+Userid)
content.subscribe(data => {
console.log(data);
this.contents=data;
console.log(this.contents);
})
Well, the main problem here is that when you check this statement:
if (this.contents) { ... }
It will always go to else.
Why? Because the async operation where you define this.contents isn't resolved yet, and as you didn't defined an initial value for this.contents, it's undefined.
For a deep explanation I'd suggest you to check this question.
That said, I want to suggest another approach to solve your problem:
Instead of having the if/else with full duplicate code, let's break it into a method, like this:
initForm(data: Object = {}) {
this.complexForm = fb.group({
firstName: [data.firstName, Validators.required],
lastName: [data.lastName, [Validators.required, Validators.minLength(1), Validators.maxLength(10)],
gender: [data.gender, Validators.required],
email: [data.email, Validators.required],
contact_number: [data.contact_number, [Validators.required, Validators.minLength(10), Validators.maxLength(10)],
experience: [data.experience, Validators.required],
skills: [data.skills, Validators.required],
notice_period: [data.notice_period, Validators.required]
});
}
Explanation:
In the signature method I'm initializing data as a clean object, so if nothing or undefined | null is passed to the function it'll automatically become a clean object like this {}.
It's useful because it prevents the undefined.<property> errors.
Full code:
constructor(private fb: FormBuilder,
private router: Router,
private db: AngularFireDatabase,
private firebaseService:FirebseService) {
const userId = localStorage.getItem('user');
if (userId) {
this.db.object(`/candidates_list/${userId}`)
.subscribe(data => {
this.contents = data; // This variable isn't used anymore.
this.initForm(data);
});
} else {
this.initForm();
}
}
initForm(data: Object = {}) {
this.complexForm = fb.group({
firstName: [data.firstName, Validators.required],
lastName: [data.lastName, [Validators.required, Validators.minLength(1), Validators.maxLength(10)],
gender: [data.gender, Validators.required],
email: [data.email, Validators.required],
contact_number: [data.contact_number, [Validators.required, Validators.minLength(10), Validators.maxLength(10)],
experience: [data.experience, Validators.required],
skills: [data.skills, Validators.required],
notice_period: [data.notice_period, Validators.required]
});
}
Notes:
1 - The Validators.compose isn't needed, you can just pass an array, or if it's a single validator, the validator itself, in both parameters (2nd and 3rd).
2 - I'd suggest you to move this code from constructor to ngOnInit.
3 - You can face some error in template since the complexForm won't be filled until the async operation is resolved (when you have a user stored, of couse).
To be more specific it'll throw this error:
ORIGINAL EXCEPTION: formGroup expects a FormGroup instance. Please
pass one in.
So, you'll have to use some flag to handle the form in template.
Supposing that you've something like this in your template:
<form [formGroup]="complexForm">
...
</form>
You can solve solve this doing the following:
Component:
import 'rxjs/add/operator/finally';
...
isLoading: boolean = true;
...
this.db.object(`/candidates_list/${userId}`)
.finally(() => this.isLoading = false)
.subscribe(data => {
this.contents = data;
this.initForm(data);
});
Template:
<ng-container *ngIf="!isLoading">
<form [formGroup]="complexForm">
...
</form>
</ng-container>

Form asynchronous validation in angular2

Am new to angular2 and i would like to validate an email address from the server but it fails
This is my form
this.userform = this._formbuilder.group({
email: ['', [Validators.required], [this._validationService.emailExistsValidator.bind(this)]],
});
Then the _ValidationService i have
The validation service is a service with #Injector
#Injectable()
export class ValidationService{
constructor(
private _authService:AuthService
){}
emailExistsValidator(control) {
if (control.value != undefined) {
return this._authService.checkExists("email")
.map(response => {
if (!response) {
return {'emailNotExists': true};
}
});
}
}
}
And in the authservice i have (which is another service performing the http requests)
#Injectable()
export class AuthService {
checkExists(value):Observable<any>{
return this._http.get(this.authurl+value)
.map(response => {
return response //this is either true or false
});
}
}
I have followed This link in setting up my form but it fails
The error returned is
Cannot read property 'checkExists' of undefined
at UsersComponent.webpackJsonp.187.
ValidationService.emailExistsValidator
What could be wrong
If this should point to the ValidationService, bind() needs to be
this.userform = this._formbuilder.group({
email: ['', [Validators.required], [this._validationService.emailExistsValidator.bind(this._validationService)]],
});

Categories