I added pattern attribute to template
<input class="form-control" type="text" id="elementName"
[(ngModel)]="elementName" name="elementName"
(input)="onElementNameChanged()" #name="ngModel" required pattern="[A-Za-z0-9_)*\S]*" >
How to add message to display if input is not correct?
Something like wont work:
<div [hidden]="!name.errors.pattern"
class="alert alert-danger">
Name must match pattern
</div>
I strongly advise you to use ReactiveForms instead of TemplateDrivenForms as they are really good to manage validation patterns and messages.
An HTML snippet example :
<form novalidate [formGroup]="user" (ngSubmit)="onSubmit()">
<div formGroupName="account">
<md-input-container>
<input md-input type="text" placeholder="{{'USER.EMAIL' | translate}} *" formControlName="email" autocomplete="off">
<md-hint class="error" *ngIf="user.get('account.email').hasError('required') && user.get('account.email').touched">
{{'VALIDATOR.EMAIL_RQ' | translate}}
</md-hint>
<md-hint class="error" *ngIf="user.get('account.email').hasError('pattern') && user.get('account.email').touched">
{{'VALIDATOR.WRONG_EMAIL' | translate}}
</md-hint>
</md-input-container>
<md-input-container>
<input md-input type="password" placeholder="{{'USER.PASSWORD' | translate}} *" minlength="6" formControlName="password" autocomplete="off">
<md-hint class="error" *ngIf="user.get('account.password').hasError('required') && user.get('account.password').touched">
{{'VALIDATOR.PWD_RQ' | translate}}
</md-hint>
</md-input-container>
</div>
<button md-raised-button [disabled]="!user.valid || submitted" color="accent" type="submit" class="submit-button">
{{'LOGIN.BUTTON' | translate}}
</button>
</form>
This HTML example shows a login form with validation. Note how the md-hint elements show only when the form has the validation errors on the specific fields.
You can actually set an interface to match your ReactiveForm.
Here is the interface linked to the example :
export interface Account {
account: {
email: string;
password: string;
}
}
The TypeScript example snippet :
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { EmailRegex } from 'app/shared/shared.interface'; // Imported regex for validator
...
user: FormGroup;
ngOnInit() {
...
// Init your reactive form
this.user = this.formBuilder.group({
account: this.formBuilder.group({
email: ['', [Validators.required, Validators.pattern(EmailRegex)]],
password: ['', Validators.required]
})
});
}
Please note how the form groups match the interface. Also, you can set the form values there if you want to, but as you can see my fields email and password are set with an empty string value.
Note that you can also add as much validators as you want in the array linked to the field.
You can then access your form values on submit() directly from your primary FormGroup :
this.user.value.account.email
If this example wasn't good enough, I strongly suggest you read Todd Motto's guide on ReactiveForms which is really well explained :
https://toddmotto.com/angular-2-forms-reactive
Related
I have a problem with password validation, I am using a regex such as;
'(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])'
and my ng-form field is;
password: ['', [Validators.pattern('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])'), Validators.required]]
also in HTML, I get input as;
<div class="form-group">
<label for="password">Password</label>
<input type="password" formControlName="password" placeholder="******" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
<div *ngIf="submitted && f.password.errors" class="invalid-feedback">
<div *ngIf="f.password.errors">Invalid Password</div>
</div>
</div>
where f is function such as;
get f() {
return this.userForm.controls;
}
When I entered a password as: Harun123, I get invalid password error. Why this happens?
This question could be solved with a combination of these two answers:
So first of all, you would need a custom validator for checking the passwords, that could look like this
checkPasswords(group: FormGroup) { // here we have the 'passwords' group
let pass = group.controls.password.value;
let confirmPass = group.controls.confirmPass.value;
return pass === confirmPass ? null : { notSame: true }
}
and you would create a formgroup for your fields, instead of just two form controls, then mark that custom validator for your form group:
this.myForm = this.fb.group({
password: ['', [Validators.required]],
confirmPassword: ['']
}, {validator: this.checkPasswords })
and then as mentioned in other answer, the mat-error only shows if a FormControl is invalid, so you need an error state matcher:
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm |
null): boolean {
const invalidCtrl = !!(control && control.invalid && control.parent.dirty);
const invalidParent = !!(control && control.parent && control.parent.invalid
&& control.parent.dirty);
return (invalidCtrl || invalidParent);
}
}
in the above you can tweak when to show error message. I would only show message when the password field is touched. Also I would like above, remove the required validator from the confirmPassword field, since the form is not valid anyway if passwords do not match.
Then in component, create a new ErrorStateMatcher:
matcher = new MyErrorStateMatcher();
Finally, the template would look like this:
<form [formGroup]="myForm">
<mat-form-field>
<input matInput placeholder="New password" formControlName="password"
required>
<mat-error *ngIf="myForm.hasError('required', 'password')">
Please enter your new password
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Confirm password"
formControlName="confirmPassword" [errorStateMatcher]="matcher">
<mat-error *ngIf="myForm.hasError('notSame')">
Passwords do not match
</mat-error>
</mat-form-field>
</form>
Here's a demo for you with the above code: stackblitz
I am implementing dynamic forms using the angular formly module and It's working fine. What functionality I need is Initially there should be a select box which contains multiple options, Based on selection different form fields should be displayed. As I explained I have Implemented and it's working, here what my problem is If I select option 1 and If I submit the form without filling fields, form displaying validation errors that is also cool. But when I select option 2 form fields are changing, but by default, all required fields are showing errors. How can I resist this? Please suggest me.
html
<div class="row">
<mat-form-field class="col-lg-2">
<mat-select placeholder="Form For" (selectionChange)="getSelectedFormName($event)">
<mat-option value="uf001">UF001</mat-option>
<mat-option value="uf002">UF002</mat-option>
<mat-option value="uf003">UF003</mat-option>
</mat-select>
</mat-form-field>
<div class="col-lg-4">
<button type="button" class="btn btn-default btn-one" (click)="getDynamicForm()">GET FORM</button>
</div>
</div>
<form [formGroup]="form" (ngSubmit)="submit(model)" >
<formly-form [model]="model" [fields]="fields" [form]="form" *ngIf="isFormTypeSelected" >
</formly-form>
<button type="submit" class="btn btn-success">Submit</button>
</form>
ts file
getSelectedFormName(eve) {
this.isFormSaved = false;
this.form = new FormGroup({});
this.fields=[];
this.model = {};
this.parentFormName = eve.value;
}
getDynamicForm() {
this.isFormSaved = false;
this.savedFields=[];
this.getDynamicFormBasedOnSelection(this.parentFormName);
//fields getting from api call
}
getDynamicFormBasedOnSelection(formName: string) {
this.auth.getDynamicFormBasedOnSelction(formName, this.userAgencyCode).subscribe(
(result) => {
const str = JSON.stringify(result);
this.fields = JSON.parse(str);
this.isFormTypeSelected = true;
this.addEvents(this.fields);
});
}
Here I'm providing my screens which are for better understanding
Actually form.reset() just reset the form values. You need to reset the form directive too. for eg.
<form [formGroup]='authForm' (submit)='submitForm(formDirective)' #formDirective="ngForm" class="is-mat-form">
<mat-form-field>
<input matInput placeholder="Email ID" formControlName='login'>
<mat-error *ngIf="authForm.controls.login.hasError('required')">
Email is required
</mat-error>
<mat-error *ngIf="authForm.controls.login.hasError('email')">
Please enter a valid email address
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" formControlName='password' placeholder="Password">
<mat-error *ngIf="authForm.controls.password.hasError('required')">
Password is required
</mat-error>
<mat-error *ngIf="authForm.controls.password.hasError('minlength')">
Password must be minimum 6 digit long.
</mat-error>
</mat-form-field>
and .ts file is
submitForm(formDirective: FormGroupDirective){
if (this.authForm.invalid) {
console.log('form submitted')
this.authForm.reset()
return;
}
this will reset the form values only, to reset the form error we need to reset the formdirective as well.
submitForm(formDirective: FormGroupDirective){
if (this.authForm.invalid) {
console.log('form submitted')
this.authForm.reset()
formDirective.resetForm();
return;
}
I am learning Ionic at the time. But it seems like HTML5 validation is not working in Ionic.
So, i have a login form like below.
<h3> Login </h3>
<form>
<input name="emailaddress" placeholder="Enter Email Address" class="email" [(ngModel)]="userData.l_email" type="email" required />
<input name="name" placeholder="Enter Password" class="name" type="password" [(ngModel)]="userData.l_password" required />
<button class="semi-transparent-button is-blue with-border" (click)="login()">
Login
</button>
</form>
When i click on login button it didn't perform validation. As i have put required in both field but it simply submit the form.
Also email validation is not working.
I have checked How can I make validation of email in Ionic using HTML5, JS or Angular work? but it is a work around. That i want to avoid.
HTML5 form validation does not work in Ionic. Instead, you can use Angular forms.
This is a great tutorial by Josh Morony on how to do it.
In your case, you can do something like this using FormBuilder and specifying Validators for each field (for a full list of available validators, take a look at the docs).
import { Component } from '#angular/core';
import { Validators, FormBuilder, FormGroup } from '#angular/forms';
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class Login {
login: FormGroup;
submitted: boolean = false;
constructor(public formBuilder: FormBuilder) {
this.login = this.formBuilder.group({
email: ['', Validators.compose([Validators.email, Validators.required])],
password: ['', Validators.required],
});
}
onLogin() {
this.submitted = true;
if (this.login.valid) {
console.log(this.login.value);
}
}
}
In your template, use FormGroup and show your error message when the field is invalid.
<form [formGroup]="login" (ngSubmit)="onLogin()">
<ion-item>
<ion-label>Email</ion-label>
<ion-input type="email" formControlName="email"></ion-input>
</ion-item>
<p ion-text [hidden]="login.controls.email.valid || submitted === false" color="danger" padding-left>
Please enter a valid email
</p>
<ion-item>
<ion-label>Password</ion-label>
<ion-input type="password" formControlName="password"></ion-input>
</ion-item>
<p ion-text [hidden]="login.controls.password.valid || submitted === false" color="danger" padding-left>
Password is required
</p>
<button ion-button type="submit">Login</button>
</form>
I've been working with reactive form like I show in the link
https://plnkr.co/edit/ApCn3YicMjfm2vhSOudj?p=preview
this is my form
<div *ngFor="let item of data; let index = index">
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
<label>
<span>Full name</span>
<input type="text" placeholder="Name" formControlName="name">
</label>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('required')">
Name is required
</div>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('minlength')">
Minimum of 2 characters
</div>
<div formGroupName="account">
<label>
<span>Email address</span>
<input type="email" placeholder="Email" formControlName="email">
</label>
<div class="error" *ngIf="user.get('account').get('email').hasError('required') && user.get('account').get('email').touched">
Email is required
</div>
<label>
<span>Confirm address</span>
<input type="email" placeholder="Address" formControlName="confirm">
</label>
<div class="error" *ngIf="user.get('account').get('confirm').hasError('required') && user.get('account').get('confirm').touched">
Confirming email is required
</div>
</div>
<button type="submit" [disabled]="user.invalid">Sign up</button>
</form>
</div>
but my problem is that I have an ngFor, every time I submit the form it push the data to array.
How can I do if I want for example submit my first array and push the data to position 0 of my data array, if I submit my second form, it will push the data to position 1
But my second form should be empty
maybe you wanna using form array like this:
#Component({
selector: 'signup-form',
template: `
<form novalidate (ngSubmit)="onSubmit()" [formGroup]="users">
<div formArrayName="data">
<ng-container *ngFor="let user of fData.controls; index as i">
<div [formGroupName]="i">
<label>
<span>Full name</span>
<input type="text" placeholder="Name" formControlName="name">
</label>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('required')">
Name is required
</div>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('minlength')">
Minimum of 2 characters
</div>
<div formGroupName="account">
<label>
<span>Email address</span>
<input type="email" placeholder="Email" formControlName="email">
</label>
<div
class="error"
*ngIf="user.get('account').get('email').hasError('required') && user.get('account').get('email').touched">
Email is required
</div>
<label>
<span>Confirm address</span>
<input type="email" placeholder="Address" formControlName="confirm">
</label>
<div
class="error"
*ngIf="user.get('account').get('confirm').hasError('required') && user.get('account').get('confirm').touched">
Confirming email is required
</div>
</div>
<button type="submit" [disabled]="user.invalid">Sign up</button>
</div>
</ng-container>
</div>
</form>
`
})
export class SignupFormComponent implements OnInit {
user: FormGroup;
users: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.user = this.buildGroup();
this.users = this.fb.group({
data: this.fb.array([this.user])
});
}
get fData() {
return this.users.get('data') as FormArray;
}
buildGroup() {
return this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
account: this.fb.group({
email: ['', Validators.required],
confirm: ['', Validators.required]
})
});
}
onSubmit() {
this.fData.push(this.buildGroup());
const {valid, value} = this.fData;
console.log(valid, value);
}
}
basically, we're using FormArray to handle array of data, then loop through it.
in template, each time you loop through array item, Angular will store current AbstractControl in index variable (in above is i).
you can see form in action here: https://plnkr.co/edit/E5Qzm85LksSCZAloXZz5?p=preview
The API document here: https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html
you can use at, removeAt, etc to access or delete at specific index.
Because I'm fairly new with Angular2 I've probably made a mistake to do with data binding from the .html sheet to the .ts file but I can't work the current problem out. I have a form with an input field and this field has the required -and maxlength-tag. I have these restrictions display an error when not followed by using Material Design. And I have a button to post the input.
<form name="postFeedForm">
<table>
<tr>
<td class="long-td">
<md-input-container class="md-block | long-inputfield">
<input mdInput name="newDescription" [(ngModel)]="newDescription" #description placeholder="What's up?" required maxlength="150">
<md-hint align="end">{{description.value.length}} / 150</md-hint>
<div ng-messages="postFeedForm.description.$error" ng-show="postFeedForm.description.$dirty">
<div ng-message="required">Please enter a description</div>
<div ng-message="md-maxlength">Exceeded the maximum characters {{description.value.length}} / 150</div>
</div>
</md-input-container>
</td>
<td>
<button md-button class="text-upper | spikes_red" type="submit" (click)="post()">Post</button>
</td>
</tr>
</table>
</form>
I'm not sure about the "#description"-tag but without it I get an error. The problem is that I've followed different examples and I probably mixed them up. What I'm trying to do can be found here (including Code Pen): https://material.angularjs.org/latest/demo/input
The .ts file:
export class DashboardComponent {
newDescription: string;
post() {
// Post some feed
...
description: this.newDescription,
...
}
}
Source: https://github.com/angular/material2/blob/master/src/demo-app/input/input-demo.html
Now with "2.0.0-beta.3" you can use 'md-error' for error validation:
HTML:
<md-input-container>
<input mdInput placeholder="email" [formControl]="emailFormControl" required>
<md-error *ngIf="emailFormControl.hasError('required')">
This field is required
</md-error>
<md-error *ngIf="emailFormControl.hasError('pattern')">
Please enter a valid email address
</md-error>
</md-input-container>
TS:
import { FormControl, Validators } from '#angular/forms';
...
const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
....
export class PageComponent {
....
public emailFormControl = new FormControl('', [Validators.required, Validators.pattern(EMAIL_REGEX)]);
....
}
Thanks to developer033 who commented on my question, I noticed that I was using code that doesn't work in Angular2, therefore the solution:
<form name="postFeedForm">
<table>
<tr>
<td class="long-td">
<md-input-container class="md-block | long-inputfield">
<input mdInput name="newDescription" [(ngModel)]="newDescription" #description placeholder="What's up?" required maxlength="150">
<md-hint *ngIf="errorMessagePost()" [ngStyle]="{'color': 'red'}" align="start">{{errorMessagePost()}}</md-hint>
<md-hint align="end">{{description.value.length}} / 150</md-hint>
</md-input-container>
</td>
<td>
<button md-button class="text-upper | spikes_red" type="submit" (click)="post()">Post</button>
</td>
</tr>
</table>
</form>
The .ts file:
export class DashboardComponent {
newDescription: string;
errorMessagePost() {
if (this.newDescription === '') {
return 'Please enter a description';
} else {
return false;
}
}
post() {
// Post some feed
...
description: this.newDescription,
...
}
}
Source: https://github.com/angular/material2/issues/348
I also decided to not include the "Exceeded the maximum characters"-error because I've already capped my input and it's not possible to exceed it anymore. If others use my code and decide to use that error, don't forget to define your "description" in the .ts file (otherwise your .length check won't work).
export class DashboardComponent {
newDescription: string = '';