I have this code to which displaying errors on my form
&
This works and you don't have to hardcode the validations in you template like @Joes answer above.
Template:
<input id="password" placeholder="Password" type="password" formControlName="password" [(ngModel)]="password" [ngClass]="{'invalid-input': !formUserDetails.get('password').valid && formUserDetails.get('password').touched}">
<div class="validation-container">
<ng-container *ngFor="let validation of userValidationMessages.password">
<div class="invalid-message" *ngIf="formUserDetails.get('password').hasError(validation.type) && formUserDetails.get('password').touched">
{{validation.message}}
</div>
</ng-container>
</div>
CSS:
.validation-container div {
display: none;
}
.validation-container div:first-child {
display: block;
}
You can create a Custom Pipe
that checks first error equals with specified error:
CUSTOM PIPE
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'equals'
})
export class Equals implements PipeTransform {
transform(errors: any, error: any, args?: any): any {
if (!errors)
return false;
const array = Object.keys(errors);
if (array && array.length > 0)
return errors[array[0]] === error;
return false;
}
}
You can have lots of error div but just one error will be shown:
// input is form.controls.input1
<div *ngIf="input.errors | equals:input.errors.required">Required</div>
<div *ngIf="input.errors | equals:input.errors.maxlength">MaxLength</div>
<div *ngIf="input.errors | equals:input.errors.pattern">Pattern</div>
If you have consistent markup for your error message blocks, then you can use css to display only the first message and hide the rest:
css
.message-block .error-message {
// Hidden by default
display: none;
}
.message-block .error-message:first-child {
display: block;
}
markup
<div class="message-block">
<span class="error-message" *ngIf="myForm.get('email').hasError('required')">
Email is required (first-child of message block is displayed)
</span>
<span class="error-message" *ngIf="myForm.get('email').hasError('email')">
Invalid email format (error message hidden by default)
</span>
</div>
Angular2 behind the scene checks the status of the control and reacts accordingly. So if you don't want to have more validation at a time, you can logically play with AND(&&)
or/and OR(||)
or/and NOT(!)
operators.
<input [ngFormControl]="form1.controls['thing']" type="text" id="thing" #thing="ngForm">
<div *ngIf='thing.dirty && !thing.valid'>
<div class="err" *ngIf='thing.errors.required'>
Thing is required.
</div >
<div class="err" *ngIf='!thing.errors.required && thing.errors.ivalid'>
Thing is invalid.
</div >
</div>
You could create a reusable component for showing errors so you don't need to repeat this code again and again.
You could create a custom pipe to get the first element of the errors object of the validator:
@Pipe({
name: 'first'
})
export class FirstKeyPipe {
transform(obj) {
var keys = Object.keys(obj);
if (keys && keys.length>0) {
return keys[0];
}
return null;
}
}
This way you would be able to display only one error:
@Component({
selector: 'my-app',
template: `
<form>
<input [ngFormControl]="form.controls.input1">
<div *ngIf="form.controls.input1.errors">
<div *ngIf="(form.controls.input1.errors | first)==='required'">
Required
</div>
<div *ngIf="(form.controls.input1.errors | first)==='custom'">
Custom
</div>
</div>
</form>
`,
pipes: [ FirstKeyPipe ]
})
export class MyFormComponent {
constructor(private fb:FormBuilder) {
this.form = fb.group({
input1: ['', Validators.compose([Validators.required, customValidator])]
});
}
}
See this plunkr: https://plnkr.co/edit/c0CqOGuzvFHHh5K4XNnA?p=preview.
Note: agreed with Günter to create a usable component ;-) See this article for more details: