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
Related
I have a little problem using [value] in my inputs.
This is my component .html
<div class="form-group">
<mat-form-field class="example-full-width" style="padding-left: 15px;">
<input matInput [value]='value_2_' [(ngModel)]="form.name_1" placeholder="name_1"
name="name_pro_1" required>
</mat-form-field>
</div>
Everything looks good, but when I run the program, the value is shown in the corresponding input, however, being a value required by the input, it is still in red, it is solved until I add a letter to the value or remove something from the input.
I managed to solve the error by deleting
name = "name_pro_1"
but since I use NgModel I cannot remove the name because I get other errors. Like:
core.js:4352 ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.
Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">
I think the problem is [(ngModel)]="form.name_1". Try to replace that with
[(ngModel)]="testValue" and define a variable in the class with the same name and it will work.
I can't replicate the problem, but using [(ngModel)] doesn't make using [value] unnecessary? You can set the form.name_1 to the value you want on the component.ts file.
Finally i resolved the issue, that it's my solution:
First thing first, i delete the [(ngModel)] in the input of component.html, then i used (change) to call the function when user select a SKU
<div class="form-group input-sku" >
<mat-form-field class="example-full-width">
<mat-label>SKU</mat-label>
<input type="text" placeholder="Search.." id="sku_select" aria-label="number"
matInput [formControl]="myControl" [matAutocomplete]="auto"
[(ngModel)]="selectedValue" (change)="changedata_1($event)" >
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
<mat-option *ngFor="let data of filter_products | async" [value]="data.sku">{{data.sku}}</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
Input who recived the value:
<div class="form-group inputs-name">
<mat-form-field class="example-full-width">
<mat-label>Name</mat-label>
<input type="text" matInput id="nombre_1" required [value]="name_1" name="name_1" placeholder="Name" >
</mat-form-field>
</div>
And in my component.ts
changedata_1(e) {
this.nombre_1 = e.target.value;
this.DataToSave.nombre_1 = this.nombre_1;}
private _filter(value: string): Products_BD[] {
const filterValue = value.toLowerCase();
const data_ = this.DatasProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));
for(let element of data_){
const sku1= elemento.sku;
const name1 = element.name;
this.name_1 = name1;
this.DataToSave.sku_1 = sku1;
this.DataToSave.name_1 = name1;
}
return this.DataProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));}
this.filter_products = this.myControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(value))
);
I hope this solution helps! :)
As I register new user, after few seconds , the same api calls are made(I checked in terminal for data that I logged...api calls are made...not a problem in database) and a duplicate entry of that user is stored in database, it take some time before it duplicates the user. I tried making email field unique to avoid duplicate entries, but then it crashes node server due to MYSQL error for duplicate value for email key. I made user object global, Reset the form after each user, but nothing worked it only delayed duplication a little bit.
HTML Signup Form
<div class="container">
<mat-card class="card">
<mat-card-title>SIGNUP</mat-card-title>
<mat-card class="form-card">
<form [formGroup]="signupForm" #form="ngForm" (ngSubmit)="register(form)">
<mat-form-field appearance='outline'>
<mat-label>FirstName</mat-label>
<input matInput formControlName="first_name" type="text" placeholder="FirstName" required/>
<mat-error>
<div *ngIf="signupForm.get('first_name').hasError('required')">FirstName is <strong>required</strong>.</div>
</mat-error>
</mat-form-field>
<mat-form-field appearance='outline'>
<mat-label>LastName</mat-label>
<input matInput formControlName="last_name" type="text" placeholder="LastName" required/>
<mat-error>
<div *ngIf="signupForm.get('last_name').hasError('required')">LastName is <strong>required</strong>.</div>
</mat-error>
</mat-form-field>
<br>
<mat-form-field appearance='outline' class="full-width">
<mat-label>Email</mat-label>
<input matInput formControlName="email" type="email" placeholder="Email" required/>
<mat-icon matSuffix>email</mat-icon>
<mat-error>
<div *ngIf="signupForm.get('email').hasError('email')">Enter valid Email ID.</div>
<div *ngIf="signupForm.get('email').hasError('required')">Email is <strong>required</strong>.</div>
</mat-error>
</mat-form-field>
<br>
<mat-form-field appearance='outline' class="full-width">
<mat-label>Password</mat-label>
<input matInput formControlName="pass" [type]="hidePass ? 'password' : 'text'" placeholder="Password" required/>
<button mat-icon-button matSuffix (click)="hidePass = !hidePass">
<mat-icon>{{hidePass ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-error>
<div *ngIf="signupForm.get('pass').hasError('required')">Password is <strong>required</strong>.</div>
<div *ngIf="signupForm.get('pass').hasError('minlength')">Password should be <strong>atleast 6 characters long</strong>.</div>
</mat-error>
</mat-form-field>
<br>
<mat-form-field appearance='outline' class="full-width">
<mat-label>Confirm Password</mat-label>
<input matInput formControlName="conf_pass" [type]="hideConfPass ? 'password' : 'text'" placeholder="Re-enter the Password" [errorStateMatcher]="matcher" required/>
<button mat-icon-button matSuffix (click)="hideConfPass = !hideConfPass">
<mat-icon>{{hideConfPass ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-error>
<div *ngIf="signupForm.hasError('notSame')">Password & Confirm Password does not match.</div>
</mat-error>
</mat-form-field>
<br>
<mat-card-actions>
<button type="submit" mat-raised-button class="button full-width" [disabled]="signupForm.invalid">Signup</button>
</mat-card-actions>
<button mat-button class="" [routerLink]="'/login'" >Already have an account</button>
</form>
</mat-card>
</mat-card>
</div>
Function that is called on submit
register(form: NgForm){
// Make an User object to store in database.
this.user = {
firstname: this.signupForm.controls.first_name.value,
lastname: this.signupForm.controls.last_name.value,
email: this.signupForm.controls.email.value,
password: this.signupForm.controls.pass.value
}
this.authService.registerUser(this.user).subscribe((data: any) => {
if (data.statusCode === util.statusCode.OK) {
console.log(data);
}
});
console.log(this.user);
form.resetForm();
}
}
Front-End service (authService)
registerUser(user: User): Observable<any> {
const cpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
const options = {headers: cpHeaders};
return this.http.post(this.baseUrl + '/register-user', user, options)
.pipe(catchError(err => this.handleError(err)));
}
Routes
router.post('/register-user', (req, res) => {
authService.registerUser(req.body, (data) => {
res.send(data);
});
});
Back-End Service (authService)
let registerUser = (data, callback) => {
async.auto({
user: (cb) => {
bcrypt.hash(data.password, saltRounds, function(err, hash) {
var dataToSet = {
"first_name": data.firstname,
"last_name": data.lastname,
"email": data.email,
"password": hash
}
userDAO.registerUser(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode":404, "statusMessage": "Server is busy" });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.USER_ADDED, "result":dataToSet });
});
});
}
}, (err, response) => {
callback(response.user);
})
}
userDAO.registerUser()
let registerUser = (dataToSet, callback) => {
console.log("data in DAO", dataToSet);
console.log("insert into Users set ? ", dataToSet);
dbConfig.getDB().query(`insert into Users set ? `, dataToSet);
}
It shows this CORS error when it calls itself the second time in the browser console
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:3000/register-user. (Reason: CORS request did not succeed).
Http failure response for http://localhost:3000/register-user: 0 Unknown Error auth.service.ts:33:12
ERROR 0 core.js:9110:19
Setting a unique key constraint is meant to protect your datatable but it doesn't mean your server shouldn't protect your datatable too.
In your AuthService, try to retrieve any entry from your database for the provided unique email. If any entry is returned throw an error and don't call userDAO.registerUser().
Check a point where called twice or more through using console.log or debugging with the breaking point. And prevent multiple calling.
Set the email field to the unique key in DB. Throw 'duplicate' error.
Check this a link for CORS issue(https://developer.mozilla.org/ko/docs/Web/HTTP/CORS/Errors)
I hope this helps you. thanks.
I am trying to input some data in a form, but it is not giving me the touched status. Therefore, an error will always occur when sending a message back to the user.
I am using FormBuilder in my TS file to store the values from the HTML. It gives me this error regardless if I put in data or not.
I am lost.
The error
firstName: FormControl {validator: ƒ, asyncValidator: ƒ, _onCollectionChange:
ƒ, pristine: true, touched: false, …}
Value:
value: {firstName: "", .... }
I have tried to check for pristine in the ngIf condtion, but it doesn't.
Here is my HTML code:
<form [formGroup]="formInfo" (ngSubmit)="validateForm()">
<label>First Name <input type="text" maxlength="35" />
<div *ngIf="submitted && formInfo.controls.firstName.errors" class="error">
<div *ngIf="(formInfo.controls.firstName.pristine) && (formInfo.controls.firstName.errors.required)">Your first
name is required.</div>
</div>
</label>
....
</form>
And here's my TypeScript code:
// Class Attributes
formInfo: FormGroup;
submitted = false;
success = false;
constructor(private builder: FormBuilder) { }
// Form data as an object
ngOnInit() {
this.formInfo = this.builder.group({
firstName: ['', Validators.required],
....
});
}
// Validates the form
validateForm() {
this.submitted = true;
console.log(this);
console.log(this.formInfo);
if (this.formInfo.invalid) {
return;
}
this.success = true;
}
}
I just want the form to say, you need to type in a value if the user has not. Otherwise, there will be no error message.
I have added the following code to see if there is even value in my TS file.
<form [formGroup]="formInfo" (ngSubmit)="validateForm()">
<label>First Name <input type="text" maxlength="35" />
<div *ngIf="submitted && formInfo.controls.firstName.errors" class="error">
<div *ngIf="(formInfo.controls.firstName.pristine) && (formInfo.controls.firstName.errors.required)">Your first
name is required.</div>
</div>
</label>
....
</form>
<!-- I added this -->
<div *ngIf="submitted">
<strong>First Name</strong>
<span>{{ formInfo.controls.firstName.value }}</span>
</div>
It seems that the value is never saved into the TS.
formInfo.controls.firstName.pristine will be true if the user has not yet changed the value in the UI.
https://angular.io/api/forms/AbstractControl#pristine
You'll want to modify
<div *ngIf="(formInfo.controls.firstName.pristine) && (formInfo.controls.firstName.errors.required)">Your first
name is required.</div>
to be
<div *ngIf="formInfo.controls.firstName.invalid && formInfo.controls.firstName.errors.required">Your first
name is required.</div>
To Solve my question you just need to change
<form [formGroup]="formInfo" (ngSubmit)="validateForm()">
<label>First Name <input type="text" maxlength="35" />
<div *ngIf="submitted && formInfo.controls.firstName.errors" class="error">
<div *ngIf="(formInfo.controls.firstName.pristine) &&
(formInfo.controls.firstName.errors.required)">Your first
name is required.</div>
</div>
</label>
....
</form>
To
<form [formGroup]="formInfo" (ngSubmit)="validateForm()">
<label>First Name <input type="text" maxlength="35" formControlName="firstName">
<div *ngIf="submitted && formInfo.controls.firstName.errors" class="error">
<div *ngIf="(formInfo.controls.firstName.pristine) &&
(formInfo.controls.firstName.errors.required)">Your first
name is required.</div>
</div>
</label>
....
</form>
Sorry for the inconvenience...
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 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