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...
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'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.
I am trying to compare values coming from database to the value I am entering on my angular 2 model driven form. I want to display a div when the values are not equal. I am trying the logic below but I am unable to make it work. Help will be appreciated.
View
<form [formGroup]="reguserform" #f="ngForm" (ngSubmit)="register()">
<fieldset>
<div class="form-group">
<label for="Username">Username</label>
<input class="form-control"
[(ngModel)]="user.Username"
type="text" id="Username"
formControlName="Username" />
</div>
<div class="alert alert-danger"
*ngIf="reguserform.controls.Username.touched && reguserform.controls.Username.errors">
<div *ngIf="reguserform.controls.Username.errors.required"
class="alert alert-danger">
Please enter a valid Username...
</div>
<div *ngFor="let r of rusers">
<div *ngIf="r.Username == user.Username" class="alert alert-danger">Username is taken</div>
</div>
</div>
</fieldset>
Component
getUsers() {
this.authenticationService.getRegUser().subscribe(
res => this.rusers = res
);
}
Everything is fine from the db end the object is being logged in console too but at the time of comparison no div is shown.
Your idea works in general, but that *ngIf="reguserform.controls.Username.touched && reguserform.controls.Username.errors" isn't true!
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<form [formGroup]="reguserform" #f="ngForm" (ngSubmit)="register()">
<fieldset>
<div class="form-group">
<label for="Username">Username</label>
<input class="form-control"
[(ngModel)]="user.Username"
type="text" id="Username"
formControlName="Username" />
</div>
<div class="alert alert-danger">
<div *ngFor="let r of rusers">
<div *ngIf="r.Username == user.Username" class="alert alert-danger">Username is taken</div>
</div>
</div>
</fieldset>
</form>
</div>
`,
})
export class App {
public reguserform: FormGroup; // our model driven form
user = {};
rusers = [
{ Username: 'mxii' },
{ Username: 'test' },
{ Username: 'peter' }
];
constructor(private _fb: FormBuilder) {
this.name = 'Angular2'
}
ngOnInit() {
this.reguserform = this._fb.group({
Username: ['', [<any>Validators.required, <any>Validators.minLength(1)]]
});
}
}
See my live-demo: https://plnkr.co/edit/SnHfoAL2dnuwKkrFYGzE?p=preview
When you write res => this.rusers = res I guess you put the http response in your variable. It's not an iterable object. Maybe you need to write this :
getUsers() {
this.rusers = this.authenticationService.getRegUser().map(
res => res.json()
);
}
I am using the model driven form like this .
Just like normal validations , i want that i show an error message if username and password are missing.
And Submit button should be disabled as long as username and password are not valid.
<div class="login">
<form #f="ngForm" (ngSubmit)="dologin(f)">
<div class="form-group">
<label for="username">Username</label>
<input id="username" type="text" class="form-control" name ="username" ngModel #username="ngModel">
<div [hidden]="username.valid" class="alert alert-danger"> Username is required.</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input id="password" type="password" class="form-control" name ="password" ngModel #password="ngModel">
<div [hidden]="password.valid" class="alert alert-danger"> Password is required.</div>
</div>
<button type="submit" [disabled]="username.length<0 || password.length<0" class="btn btn-primary" type="submit">Login</button>
</form>
</div>
i am seeing quite strange behaviour from validation div. Sometimes
it is showing "Password is required" and sometimes not.
i want to disable the submit button, until the form is valid .i tried
[disabled]="!f.valid"
but as i print it out f is always valid even
though i have not entered any data in username and password.
Component:
constructor(private router: Router,private authenticationService : AuthenticationService,private httpService:HttpService,private formBuilder:FormBuilder) {
this.form=formBuilder.group({
username:['',Validators.required],
password:['',Validators.required]
});
}
UPDATE
Can't bind to 'formGroup' since it isn't a known property of 'form'.
(" ][formGroup]="form"
(ngSubmit)="dologin(form.value)">
][formControl]="form.controls['password']">
[ERROR ->]
Username
[ERROR ->]
"): LoginComponent#4:8 No provider for NgControl ("
Password
[ERROR ->] ; Task: Promise.then ; Value:
Error: Template parse errors:(…) Error: Template parse errors: Can't
bind to 'formGroup' since it isn't a known property of 'form'. ("
][formGroup]="form"
(ngSubmit)="dologin(form.value)">
][formControl]="form.controls['password']">
[ERROR ->]
Username
[ERROR ->]
Thanks.
The way you have set up your HTML template is missing some key bits that actually ensure you have wired up the front end to the back end for a reactive form. What you have appears to be more in line with a template driven form mixed with model driven. In fact, the template you have posted will not even compile if you remove your FormsModule import.
To begin with remove your FormsModule import which is letting you mix the two different form types together. This will take us down a path where a strict Reactive Forms (aka model driven) implementation is required.
<form #f="ngForm" (ngSubmit)="dologin(f)"> will be changed to <form [formGroup]="form" (ngSubmit="dologin(form.value)"
Each of your inputs and warning divs will change from
<input id="username" type="text" class="form-control" name="username" ngModel #username="ngModel">
<div [hidden]="username.valid" class="alert alert-danger"> Username is required.</div>
To
<input id="username" type="text" class="form-control" name="username" formControlName="username">
The changes are because the ngModel attribute and #[name]="ngModel" are not supported in the model driven form, so instead you will use either formControlName or [formControl] syntax.
<div [hidden]="form.controls['username'].valid || form.controls['username'].pristine"
class="alert alert-danger"> Username is required.</div>
Finally, your submit button changes, note that you have type="submit" twice, from <button type="submit" [disabled]="username.length<0 || password.length<0" class="btn btn-primary" type="submit">Login</button>
To
<button type="submit" [disabled]="!form.valid" class="btn btn-primary">Login</button>
since we have successfully wired up the rest of the form the validation on the form group will now be correct
And here is a working plunker that you can play around with: https://plnkr.co/edit/Mu9vEYGB35SwUr9TEsPI?p=preview
Implementation without form builder
<form #loginForm="ngForm" (ngSubmit)="login()">
<md-input required type="email"
pattern="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
placeholder="Email Address" #email="ngModel" name="email"
[(ngModel)]="loginModel.email"></md-input>
<div *ngIf="email.dirty && !email.valid && email.touched && email.errors" class="error-message">
<div *ngIf="email.errors.required">Email is required</div>
<div *ngIf="!email.errors.required && email.errors.pattern">This is not a valid email</div>
</div>
<md-input required type="password" placeholder="Password" #password="ngModel" name="password" [(ngModel)]="loginModel.password"></md-input>
<div *ngIf="password.dirty && !password.valid && password.touched && password.errors" class="error-message">
<div *ngIf="password.errors.required">Password is required</div>
</div>
<button ma-raised-button [disabled]="!loginForm.valid">
Login
</button>
</form>
Component:
ngOnInit() {
this.loginModel = {email: '', password: ''};
}
login() {
console.log(this.loginModel['email']);
console.log(this.loginModel['password']);
}
I have two methods ngMake and ngUpdate. I have one form, PostForm. I want to reuse the same form, but add different functionality depending on the url.
I gain information about the url using
Controller
if ($location.path() === '/makepost') {
$scope.FormTitle = 'Make a Post';
$scope.FormAction = 'server/blog/makepost.php';
$scope.FormMethod = 'POST';
$scope.FormSubmit = "ngMake()"
};
if ($location.path().indexOf('update') !== -1) {
$scope.FormTitle = 'Update a Post';
$scope.FormAction = null;
$scope.FormMethod = 'POST';
$scope.FormSubmit = "ngUpdate()";
};
HTML Form
<div ng-controller="BlogController as blog">
<h3 class="text-center">{{FormTitle}}</h3>
<form ng-show='user != null' ng-submit="{{FormSubmit}}" role="form" class="form-group" name="PostForm">
<label>Title: </label>
<div ng-class="(PostForm.Title.$dirty && PostForm.Title.$invalid) ? 'has-warning' : 'has-success'" class="form-group has-feedback">
<input data-ng-model="post.Title" data-ng-minlength="3" data-ng-maxlength="255" name="Title" type="text" class="form-control" placeholder="Title" required/>
<span ng-class="(PostForm.Title.$dirty && PostForm.Title.$invalid) ? 'glyphicon-warning-sign' : 'glyphicon-ok'" class="glyphicon form-control-feedback"></span>
</div>
<label>Content: </label>
<div ng-class="(PostForm.Content.$dirty && PostForm.Content.$invalid) ? 'has-warning' : 'has-success'" class="form-group has-feedback">
<textarea data-ng-model="post.Content" rows="8" name="Content" type="text" class="form-control" placeholder="Content" required></textarea>
<span ng-class="(PostForm.Content.$dirty && PostForm.Content.$invalid) ? 'glyphicon-warning-sign' : 'glyphicon-ok'" class="glyphicon form-control-feedback"></span>
</div>
<div ng-controller="AuthController as auth">
<input class="ng-hide" type="number" data-ng-model="post.UserID" name="UserID" value="{{user.ID}}">
</div>
<br>
<input type="submit" ng-class="(PostForm.$valid) ? 'btn-success' : 'disabled'" class="btn btn-block btn-default">
</form>
<p ng-show='user == null' class="text-center">You must be logged in in order to {{FormTitle | lowercase}}</p>
Explination
The {{FormSubmit}} template variable probably executes afterwards and causes a problem which doesn't permit the form to execute. I am open to suggestions, I want to reuse the same form. I read that ngSubmit requires a type="submit" button or input element contained within the form tags, I have that. I do not have any ng-clicks which might hinder the form.
I am open to suggestions
If there are any other problems with the form or the entire project please let me know, even if it is just a "bette practice".
The full project
https://github.com/AquaSolid/RAMA_Angular_PHP
Basically, I wised up. I contained the logic in the back-end. I created a function to choose which function to use. Meanwhile the the form contains the attribute ng-submit="chooseSubmit()". That's about it..
$scope.chooseSubmit = function() {
if ($scope.FormSubmit) {
if ($scope.FormSubmit === 'ngMake()') {
$scope.ngMake();
} else {
$scope.ngUpdate();
};
}
};