Ionic2, Angular, Form validation with API calls - javascript

How to use form validation with API calls to check if data is correct? Whenever I enter this Signup page I got errors like:
Cannot read property 'put' of undefined
Cannot read property 'http' of undefined
Could you guide me what is wrong in my code?
HTML:
<ion-content padding>
<p *ngIf="submitAttempt" style="color: #ea6153;">Please fill out all details accurately.</p>
<ion-list inset>
<form [formGroup]="signupForm">
<ion-item>
<ion-input formControlName="userName" [(ngModel)]="userName" placeholder="UserName" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-input formControlName="email" [(ngModel)]="email" placeholder="Email" type="email"></ion-input>
</ion-item>
<ion-item>
<ion-input formControlName="password" [(ngModel)]="password" placeholder="Password" type="password"></ion-input>
</ion-item>
</form>
<button ion-button block (click)="register()">Register</button>
</ion-list>
</ion-content>
And this is my TS file, this code is inside constructor:
this.signupForm = formBuilder.group({
userName: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*')]), this.authService.checkUserName],
email: ['', this.authService.checkEmail],
password: ['']
});
this.authService is a class with API calls, here is one of the call, seconds looks same just with different address:
checkUserName(control: FormControl): any {
return new Promise((resolve, reject) => {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http.put('http://localhost:63203/api/Customers/CheckIfUserExist', JSON.stringify(control.value), { headers: headers })
.subscribe((res) => {
resolve(res);
}, (err) => {
reject(err);
});
});
}

you should use array function to keep the original context.
this.signupForm = formBuilder.group({
userName: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*')]), () => this.authService.checkUserName()],
email: ['', () => this.authService.checkEmail()],
password: ['']
});

Related

Unique username validation in Angular with data returns type of observable from service

// register-page.ts
this.registerationForm = new FormGroup(
{
username: new FormControl(null,
[
Validators.required,
Validators.minLength(3),
Validators.maxLength(30),
Validators.pattern('^[a-zA-Z-0123456789]*$'),
]
),
// accountService.ts
validateUsername(username: string): Observable<any> {
return this.httpManager.post(authServer + "username-validator", new ValidateUsernameRequest(username)).pipe(
map(
(response: Response) => {
return response.data;
}
)
);
}
// register-page.html
<ion-item [ngClass]="username==null ? 'at-beginning':''">
<ion-label position="floating">Kullanıcı Adı</ion-label>
<ion-input name="username" formControlName="username" inputmode="text" class="ion-text-lowercase"
placeholder="Kullanıcı Adı" (onchange)=(checkUsername($event)) (keypress)=(onKeyUp($event)) (keydown.space)="$event.preventDefault()">
</ion-input>
</ion-item>
<div class="err" *ngIf="formControls.username.errors">
<div *ngIf="formControls.username.errors.required">Kullanıcı adı zorunlu.</div>
<div *ngIf="formControls.username.errors.minlength">Kullanıcı adınız çok kısa.</div>
<div *ngIf="formControls.username.errors.maxlength">Kullanıcı adınız çok uzun.</div>
<div *ngIf="formControls.username.errors.pattern">Kullanıcı adınız özel karakter içeremez.</div>
<div *ngIf="formControls.username.errors.checkUsername">Kullanıcı adınız alınmış.</div>
</div>
I've tried to code a validator for the username that checks its availability whenever the user make changes on the input. I've fight for it two days but I'm just stuck. I understood that I need to use an async function that subscribes the data came from accountService.validateUseranem(control.value) but somehow I failed to make it work.
Can someone help me on this?
I did the following and fixed my own problem.
Created username-validator.directive.ts
import { AbstractControl, AsyncValidatorFn, ValidationErrors } from '#angular/forms';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { AccountService } from 'src/app/services/yon/auth/account.service';
export function existingUsernameValidator(userService: AccountService): AsyncValidatorFn {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
return userService.validateUsername(control.value).pipe(map(
(data) => {
console.log("The result is : " + data)
return (!data) ? { "usernameExists": true } : null;
}
));
};
}
Then used it inside of the validators of username
this.registerationForm = new FormGroup(
{
username: new FormControl(null, {
validators: [
Validators.required,
Validators.minLength(3),
Validators.maxLength(20),
],
asyncValidators: [existingUsernameValidator(this.accountService)],
updateOn: 'change'
}),
email: new FormControl(null, {
validators: [
Validators.required,
Validators.email],
asyncValidators: [existingEmailValidator(this.accountService)],
updateOn: 'change'
}),
like this
Also;
updateOn: 'change'
Understands if the input changes on that control and whenever the input changes, it checks the value for validation.
And becasue that I need to send request to API and use that value returns as a response , my validator needed to be an asynchronous validator. Also the sync and aysnc validators need to be splitted as far as I understand, like I did.

Dynamic ion-select-options after receiving data from axios promise

I wonder if there is possibility to dynamically add ion-select-option to ion-select after axios promise returns number of options, for example if axios promise returns x=5 then add 5 oprions to already created ion-select.
Here is ion-select code (there is single ion-select-option):
<ion-item>
<ion-label>Some label</ion-label>
<ion-select class="some class" value="1" interface="popover">
<ion-select-option value="1">1</ion-select-option>
</ion-select>
</ion-item>
Here is axios function which is triggered on button click:
methods: {
onClickFunction(){
axios.post("http://some_php.php", formData)
.then(res => {
x = res.data[2]; // <-- this is number of options
// here I want to add x * ion-select-option
}
}
If anybody wonder, I found solution.
<ion-item>
<ion-label>Some label</ion-label>
<ion-select placeholder="1" v-model="selectedOption" interface="popover">
<ion-select-option v-for="(item, index) in tab" :key="index"
:value="item">{{item}}</ion-select-option>
</ion-select>
</ion-item>
data() {
return {
tab: []
},
created() {
onClickFunction(){
axios.post("http://some_php.php", formData)
.then(res => {
x = res.data[2];
for (let i = 1; i <= res.data[2]; i++){
this.tab.push(i);
}
}
}
This works as I expected.

How to push more than one value into the form?

I want to push an array of inputs into a form. At the moment I always get with a console.logonly the latest inputted value. How can I push all the input values? I just wonder If I even need a additional form arrays. Since I can output the whole list in my console. So I have access to this data which is imported in order to upload to a server.
page.html
<form [formGroup]="form" (ngSubmit)="addTag(form.value)">
<ion-item>
<ion-input formControlName="tag" clearInput="true" placeholder="Tags" [(ngModel)]="tagInput" name="tagValue"></ion-input>
<ion-button item-right type="submit" icon-only>
<ion-icon name="checkmark"></ion-icon>
</ion-button>
</ion-item>
</form>
<ion-chip *ngFor="let tag of tagList; let i = index">
<ion-icon name="pricetag"></ion-icon>
<ion-label>{{ tag }}</ion-label>
<ion-icon name="close-circle" (click)="removeChip(i)"></ion-icon>
</ion-chip>
page.ts
form: FormGroup;
public tagList: any[] = [];
constructor() { }
addTag(formValue) {
if (this.tagInput !== '') { //no empty input
this.tagList.push(formValue.tagValue);
this.tagInput = '';
}
}
ngOnInit() {
this.form = new FormGroup({
tag: new FormControl(null, {
updateOn: 'submit',
validators: [Validators.required, Validators.maxLength(20), Validators.minLength(1)]
})
});
}
confirm() {
console.log(this.form);
}
so, based on your code, you actually have a form and an array of items added from the form... not sure why you need a form array or anything like that. Your fixed code could just be like this:
<form [formGroup]="form" (ngSubmit)="addTag()">
<ion-item>
<ion-input formControlName="tag" clearInput="true" placeholder="Tags" name="tagValue"></ion-input>
<ion-button item-right type="submit" icon-only>
<ion-icon name="checkmark"></ion-icon>
</ion-button>
</ion-item>
</form>
<ion-chip *ngFor="let tag of tagList; let i = index">
<ion-icon name="pricetag"></ion-icon>
<ion-label>{{ tag }}</ion-label>
<ion-icon name="close-circle" (click)="removeChip(i)"></ion-icon>
</ion-chip>
get rid of the mixing of reactive forms and template forms, and just call add tag, don't pass a value in.
form: FormGroup;
public tagList: any[] = [];
constructor() { }
addTag() { // properly access and reset reactive form values
const tagCtrl = this.form.get('tag');
if (tagCtrl.value) {
this.tagList.push(tagCtrl.value);
this.tagCtrl.reset(''); // reset() sets the value and resets validation
}
}
ngOnInit() {
this.form = new FormGroup({
tag: new FormControl(null, {
updateOn: 'submit',
validators: [Validators.required, Validators.maxLength(20), Validators.minLength(1)]
})
});
}
confirm() {
console.log(this.tagList); // just check your tagList instead of looking at the form
}
you're overthinking it here. a FormArray could be useful under some circumstances, like needing some complex validation / error message capability, or ability to edit tags after adding them, but if simple remove is all you need, you're over engineering this.

Getting a 'Path Error' with nested forms Array

I have the following html:
<form [formGroup]="customFieldForm">
..........
<div formArrayName="FieldNames">
<div *ngFor="let fieldName of customFieldForm.get('FieldNames').controls; let i = index"
[formGroupName]="i">
<mat-form-field>
<input matInput [value]="fieldName.value.CustomFieldName"
[formControlName]="CustomFieldName"
[placeholder]="placeHolderInLocalLanguage">
</mat-form-field>
</div>
</form
I'm getting the following error:
Cannot find control with path: 'FieldNames -> 0 (and all the length of the array...) ->
the TS file :
this.customFieldForm = new FormGroup({
Menu: new FormControl(null, Validators.required),
FieldType: new FormControl(null, Validators.required),
FieldNames : new FormArray([]),
OptionalOrMendatory: new FormControl(false),
LineTypes: new FormControl(null),
})
the Initiation Of data from server:
this.data.forEach(nameAndLanguage => {
const FieldName: FormGroup = this.fb.group({
CustomFieldName: nameAndLanguage.CustomFieldName,
ID: nameAndLanguage.LanguageID
});
(<FormArray>this.customFieldForm.get('FieldNames')).push(FieldName);
})
Any one can spot what I'm doing wrong here?
Thanks to anyone trying to help in advance!
I found my issue :
[formControlName]="CustomFieldName"
tried by mistake to bind to a property.

Angular FormGroup Error doesnt get displayed

I have the problem that my App doesnt diplay the errors.
This is my html:
<ion-item>
<ion-label floating style="margin-left: 20px;">Username</ion-label>
<ion-input type="text" name="username" formControlName="username" [(ngModel)]="registerCredentials.username" required></ion-input>
</ion-item>
<ion-item *ngIf="!registerForm.get('username').valid && (registerForm.get('username').dirty)">
<p style="color: white;" *ngIf="registerForm.get('username').hasError('taken')">funktioniert</p>
<p style="color: white;" *ngIf="registerForm.get('username').hasError('min')">funktioniert</p>
<p style="color: white;" *ngIf="registerForm.get('username').hasError('max')">funktioniert</p>
<p style="color: white;" *ngIf="registerForm.get('username').hasError('latein')">funktioniert</p>
</ion-item>
this is my ts:
registerForm: FormGroup;
this.registerForm = formBuilder.group({
username: ['', Validators.compose([Validators.minLength(3), Validators.maxLength(15), Validators.pattern('^[A-Za-z0-9]+$'), UsernameValidator.checkUsername, Validators.required])],
email: ['', Validators.compose([Validators.pattern('^[a-zA-Z0-9.!#$%’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$'), EmailValidator.checkEmail, Validators.required])],
password: ['', Validators.compose([Validators.minLength(8), Validators.maxLength(20), Validators.pattern('^[^-\s][a-zA-Z0-9_\s-]+$'),PasswordValidator.checkPassword, Validators.required])]
});
and this is my custom UsernameValidator:
export class UsernameValidator {
static checkUsername(control: FormControl): any {
var onlyLetters;
return new Promise(resolve => {
if(control.value.toLowerCase() === "greg"){
resolve({
"taken": true
});
} else if (control.value.length < 3){
resolve({
"min": true
});
} else if (control.value.length > 8){
resolve({
"max": true
});
} else if (!(onlyLetters = /^[a-zA-Z\u00C0-\u00ff]+$/.test(control.value))){
resolve({
"latein": true
});
} else {
resolve(null);
}
});
}
}
this custom Validator should return the error Keys. In the html I want to check for the error keys and display the message if an error is true and the key got returned. But it doesnt seem to work. The Validation itselfe works. But ErrorMessages dont work.
Your validator for checking if the username is taken, is an async validator. Async validators should be passed as the third argument.
so change your assignment of your username formcontrol to:
username: ['', [sync validators here], [UsernameValidator.checkUsername]]
** also Validators.compose is not needed.
DEMO
as a non related issue, I would drop the [(ngModel)] and make use of the form control. You can set the initial value to your form control instead. If this arrives later, you can use setValue():
this.registrationForm.setValue({
username: this.registerCredentials.username
// setting other possible values to your form controls
})
and remove the ngModel altogether. Having two bindings (ngModel and FormControl) can cause issues.

Categories

Resources