I just discovered Angular and Material design, and I really love all the features that come out of the box. I created a contact form with validation, which gives error messages when a field is required or is put in the incorrect format. The only thing that is missing is that when the user clicks the "submit" button, they can still send the form to the server. So the error messages don't really help.
HTML:
<md-content layout-padding="">
<form name="projectForm" action="/" method="post" novalidate>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm="">
<label>Email</label>
<input required="" type="email" name="emailAddress" ng-model="project.emailAddress" ng-pattern="/^.+#.+\..+$/">
<div ng-messages="projectForm.emailAddress.$error" role="alert">
<div ng-message-exp="['required', 'pattern']">
Your email must be in correct format and look like an email address.
</div>
</div>
</md-input-container>
</div>
<md-input-container class="md-block">
<label>Message</label>
<textarea required="" name="content" md-maxlength="50" ng-model="project.content" rows="3" md-select-on-focus=""></textarea>
<div ng-messages="projectForm.content.$error" role="alert">
<div ng-message="required">
Your must enter a message.
</div>
</div>
</md-input-container>
<md-button type="submit" class="submit">Submit</md-button>
JS:
angular
.module('toast', ['ngMaterial', 'ngMessages'])
.controller('formCtrl', function($scope) {
$scope.project = {
//scope stuff goes here
};
})
Here is the Codepen.
Disable the submit button when the form is invalid:
<md-button type="submit" class="submit" ng-disabled="projectForm.$invalid">Submit</md-button>
This you can try
For angular latest you can validate your form in type script using simple below code
Suppose your form looks like this
<form [formGroup]="userForm" novalidate (ngSubmit)="onSubmit()">
you can validate its state like
if (!this.userForm.valid) {
return;
}
you can use also add [disabled]="projectForm.$invalid"
to:
Submit
Related
I have two forms on a page that are identical, but I'm trying to validate the one field (which is email in this case), but I can't seem to get it to just validate the one input field as it just shows the error for both forms.
HTML:
<div class="general-form">
<div class="email-error" style="display:none;">
<p>You need valid email</p>
</div>
<div class="form-wrap">
<div class="form-row">
<input id="from-email" type="email" name="email" placeholder="Your Email" />
</div>
<div class="btn-row">
<button class="submit-btn">Submit</button>
</div>
</div>
</div>
<div class="general-form">
<div class="email-error" style="display:none;">
<p>You need valid email</p>
</div>
<div class="form-wrap">
<div class="form-row">
<input id="from-email" type="email" name="email" placeholder="Your Email" />
</div>
<div class="btn-row">
<button class="submit-btn">Submit</button>
</div>
</div>
</div>
JS:
$(".submit-btn").on("click", function() {
var $this = $(this);
var valid_email = $this.find("#from-email").val();
if (/(.+)#(.+){2,}\.(.+){2,}/.test(valid_email)) {
return true;
} else {
$this.parents().find(".email-error").show();
return false;
}
});
Overall, I can get it to pass through the validation, but the error message shows for both forms and I'm not sure how to get it so it only shows the error message for that particular form. I'm guessing that I'm pushing too far up the chain and it's testing for both of the forms, but I can't remember which one to target specifically if that makes any sense.
You doubled the id from-email that’s why. In your JS you are checking all fields with the id from-email in this case both of the inputs are checked because both the id.
If one of them is wrong you are searching for the email-error in all of your parents which will go up to the body and then find all off the error wrappers. $this.parents(“.general-form“) will do the deal and only go up to the wrapper of the input and error in your case.
Always make sure your id’s are unique.
Just add required> attribute and add this js
$("#formid").validate();
I am new to angular let say, i have reactive form like follow
ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
email: [ '', Validators.compose([Validators.required, Validators.email])],
password: this.formBuilder.group({
first: [ '', Validators.required ],
second: [ '', Validators.required, ]
})
});
}
and my angular template looks like follow
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email"
class="form-control">
</div>
<div *ngIf="!registerFormGroup.get('password').get('first').valid"
class="alert alert-danger">
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-success btn-lg"
[disabled]="!registerFormGroup.valid">Submit</button>
</div>
For example, email field has two validations such as required and email type validate so depends upon the error I have to display error message so in my template am using like
<div *ngIf="!registerFormGroup.get('email').valid && (registerFormGroup.get('email').touched)"
class="alert alert-danger">
</div>
Instead of adding same registerFormGroup.get('email') again and again i trying to create template expression like #emailForm="registerFormGroup.get('email')" in
<input type="email" formControlName="email" placeholder="Enter Email" class="form-control" #emailForm="registerFormGroup.get('email')">
so that i can use use like
<div *ngIf="!emailForm.valid" class="alert alert-danger">
</div>
but i am getting error like
compiler.es5.js:1690 Uncaught Error: Template parse errors:
There is no directive with "exportAs" set to "registerFormGroup.get('email')" ("l>
]#emailForm="registerFormGroup.get('email')">
what mistake i made??
You can create common function to access form like below:
validateFormControl(controName: string) {
let control = registerFormGroup.get(controName);
return control.invalid && control.touched;
}
In Templete use this call wherever you need, you just need to pass your control name in function, you can modify this function as per your need as well and you do not need to use form.get all the time. This makes your template more cleaner and performance efficient.
<div *ngIf="validateFormControl('email')"
class="alert alert-danger">
error message
</div>
<div *ngIf="validateFormControl('password')"
class="alert alert-danger">
error message
</div>
Create a method on the component that returns if the form is valid or not and return it in the component
checkError(){ // either you can pass the form from the template or use the component for decleration
return registerFormGroup.get('email').valid;
}
In the template call
<div *ngIf="checkError()" class="alert alert-danger">
// Always make sure not to use controls in the template it will result in AOT compilation error
</div>
try this :
component.html
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" [ngClass]="{'has-error':!registerFormGroup.controls['email'].valid}">
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email"
class="form-control">
<p class="alert alert-danger" *ngIf="registerFormGroup.controls['email'].dirty && !registerFormGroup.controls['email'].valid">Invalid email address</p>
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" [disabled]="!registerFormGroup.valid">Submit</button>
</div>
</form>
</div>
component.ts
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
export class AppComponent implements OnInit {
registerFormGroup: any;
constructor(
private formBuilder: FormBuilder
) {}
ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
email: [null , Validators.compose([Validators.required, Validators.email])]
});
}
}
Angular team strongly discourage from using functions outputs in templates due to change detection strategy. You might be interested in following solution, using ngForm directive:
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email "
class="form-control" #email="ngForm">
</div>
<div *ngIf="email.invalid"
class="alert alert-danger">
</div>
It's still a hush to copy-paste it in template, but at least you might have direct reference to control via it's template variable. I personally go with controller getter function still, but I'm posting this answer for sake of answer completeness.
Although I agree with the accepted answer (you should have a dedicated method in your form component that will encapsulate the validation process)
sometimes you need a quick check in the template:
The trick is to expose the formGroup from your component and use it like so:
Template:
<input id="name" class="form-control"
formControlName="name" required
[class.is-invalid]="
f.name.invalid &&
(f.name.touched ||
f.name.touched.dirty)">
Component:
//easily access your form fields
get f() { return this.checkoutForm.controls; }
I was also looking for a more elegant solution than calling form.get multiple times. Here is what I come up with using ngif
<div class="col-sm-6" *ngIf="form.get('sponsor') as sponsor">
<input type="text" formControlName="sponsor" id="sponsor" class="form-control" name="sponsor"
[class.is-invalid]="sponsor.errors && sponsor.touched">
</div>
using the as feature on ngIf to create a template variable
I went through lots of post, but did not find what I was looking for.
Basically,
I am showing user validation on form changes. My template looks like this:
<div class="form-group"
[class.error]="!loginForm.find('email').valid && loginForm.find('email').touched">
<div class="input-wrapper">
<input type ="email"
class="form-control"
id="email-input"
placeholder="Email"
formControlName="email">
</div>
<div *ngIf="loginForm.controls['email'].dirty && !loginForm.controls['email'].valid"
class="alert-msg">Email is invalid</div>
</div>
And, looking at other posts, my TS which debounces form is this:
this.loginForm.valueChanges.debounceTime(500).subscribe(form => {
console.log(form, this.loginForm);
});
Now, the console logs are actually debouncing. But, the validation message does not debounce. It shows up straight away the message.
Is there a way to overcome this issue?
Thanks, for stopping by,
I believe the debounceTime will only affect the code you have in the response form => { ... }.
So what you could do is set the validation there:
private loginFormIsValid:boolean;
private emailIsNotValid:boolean;
ngOnInit() {
this.loginForm.valueChanges.debounceTime(500).subscribe(form => {
this.loginFormIsValid = !loginForm.find('email').valid
&& loginForm.find('email').touched;
this.emailIsNotValid = loginForm.controls['email'].dirty
&& !loginForm.controls['email'].valid;
console.log(form, this.loginForm);
});
}
...And then you would use it in you template as follows:
<div class="form-group" [class.error]="!loginFormIsValid">
<div class="input-wrapper">
<input type ="email"
class="form-control"
id="email-input"
placeholder="Email"
formControlName="email">
</div>
<div *ngIf="emailIsNotValid"
class="alert-msg">Email is invalid</div>
</div>
You can take a look at a post on debouncing, it's a good example.
I'm trying to use Angular's built-in form functions, specifically setPristine() to clear the form on user submit. My controller has access to $scope.newForm (my form) with all of its methods, but running $scope.newForm.$setPristine() isn't resetting the form fields.
Here is my HTML:
<div ng-controller="NewFormController">
<h3>New Entry</h3>
<form name="newForm" method="post" novalidate>
<div class="input-group">
<label>Name</label>
<input name="name" type="text" ng-model="place.name"/>
</div>
<div class="input-group">
<label>Description</label>
<textarea name="description" type="text" ng-model="place.description"></textarea>
</div>
<div class="input-group">
<label>Neighborhood</label>
<input name="neighborhood" type="text" ng-model="place.neighborhood"/>
</div>
<div class="input-group">
<label>Address</label>
<input name="location" type="text" ng-model="place.address"/>
</div>
<input type="submit" value="Submit" ng-click="submit(place)"/>
</form>
</div>
And here is the controller where I call setPristine():
app.controller('NewFormController', function($scope, $compile) {
$scope.place = {
name: 'ExamplePlace',
description: 'This is a description!',
neighborhood: 'Manhattan',
address: '112 Street Place'
};
$scope.submit = function(place) {
$scope.newForm.$setPristine();
$scope.newForm.$setUntouched();
};
});
Here is a working codepen that reproduces my problem.
Note: I'm using Angular version 1.4.3.
$setPristine only marks the form as being $pristine, which is useful for validation-driven expressions and CSS (e.g. .ng-dirty)
So, $setPristine does not clear the form's controls. In fact, it wouldn't even know how to do that. Consider, that to "clear" could mean different things to different models. "Clear" could mean "", or undefined, or null, or anything at all that a custom input control that works with ngModel could mean.
So, to properly clear the form is to modify the View Model that drives the form to whatever definition of "clear" it needs. In most cases - yours included - it is just a matter of setting the View Model to a new object:
$scope.submit = function(place) {
$scope.newForm.$setPristine();
$scope.newForm.$setUntouched();
// clear the form
$scope.place = {};
};
I've gotten an example of form validation working correctly in my app for the login page, looking something like this:
<div class="form-group" >
<label for="password">Password</label>
<input type="password"
id="password"
name="password"
class="form-control"
placeholder="Create password"
ng-model="password"
required
minlength="{{passwordLength}}"
>
<div class="help-block text-danger"
ng-messages="form.password.$error"
ng-if="form.password.$touched || form.$submitted"
>
<div class="text-danger">
<div ng-message="required">A password is required</div>
<div ng-message="minlength" >Password must be at least {{passwordLength}} characters</div>
</div>
</div>
</div>
This works great for "required" and "minlength" checks, which can be done before submission. However after submission, we can get back a 401 for an invalid username/password. Angular 1.3 has an async validaiton concept, but it seems to run before submission, and it seems unreasonable to try to login twice (once just to see if they could and prompt an error)
Is there a reasonable way to use the ng-messages concept to display this post-submission error?
I figured it out. You can just manually validate/invalidate the form:
<div class="help-block text-danger">
<div class="text-danger"
ng-messages="form.password.$error"
ng-if="form.password.$touched || form.$submitted">
<div ng-message="required">Password is required</div>
<div ng-message="minlength">*Password must be at least {{passwordLength}} characters</div>
<div ng-message="correctPassword">Username/Password combination is incorrect</div>
</div>
</div>
and in your controller, on submit:
$scope.login = function(){
$scope.form.password.$setValidity("correctPassword", true);
// ...do some stuff...
$http.post('/someUrl', {msg:'hello word!'})
.success(function(){})
.error(function() {
$scope.form.password.$setValidity("correctPassword", false);
});
}