Provide valuable, actionable feedback to your users with HTML5 form validation, via browser default behaviors or custom styles and Angular Forms Validation.
Custom styles
For custom CoreUI form validation messages, you’ll need to add the noValidate boolean property to your form. This
disables the browser default feedback tooltips, but still provides access to the form validation APIs in JavaScript.
Try to submit the form below; our JavaScript will intercept the submit button and relay feedback to you. When
attempting to submit, you’ll see the :invalid and :valid styles applied to your form controls.
Custom feedback styles apply custom colors, borders, focus styles, and background icons to better communicate feedback.
import { Component, signal } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
ButtonDirective,
ColComponent,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormDirective,
FormFeedbackComponent,
FormLabelDirective,
FormSelectDirective,
GutterDirective,
InputGroupComponent,
InputGroupTextDirective,
RowDirective
} from '@coreui/angular';
@Component({
selector: 'docs-validation-custom-styles',
templateUrl: './validation-custom-styles.component.html',
imports: [
ReactiveFormsModule,
FormsModule,
FormDirective,
RowDirective,
GutterDirective,
ColComponent,
FormLabelDirective,
FormControlDirective,
FormFeedbackComponent,
InputGroupComponent,
InputGroupTextDirective,
FormSelectDirective,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
ButtonDirective
]
})
export class ValidationCustomStylesComponent {
readonly customStylesValidated = signal(false);
onSubmit1() {
this.customStylesValidated.set(true);
console.log('Submit... 1');
}
onReset1() {
this.customStylesValidated.set(false);
console.log('Reset... 1');
}
}<form #customStylesForm="ngForm"
(ngSubmit)="onSubmit1()"
[gutter]="3"
[validated]="customStylesValidated()"
novalidate
cForm cRow
class="needs-validation"
>
<c-col [md]="4">
<label cLabel for="validationCustom01">First name</label>
<input cFormControl id="validationCustom01" required type="text" value="Mark" />
<c-form-feedback [valid]="true">Looks good!</c-form-feedback>
</c-col>
<c-col [md]="4">
<label cLabel for="validationCustom02">Last name</label>
<input cFormControl id="validationCustom02" required type="text" value="Otto" />
<c-form-feedback [valid]="true">Looks good!</c-form-feedback>
</c-col>
<c-col [md]="4">
<label cLabel for="validationCustomUsername">Username</label>
<c-input-group class="has-validation">
<span cInputGroupText id="inputGroupPrepend">@</span>
<input aria-describedby="inputGroupPrepend"
cFormControl
id="validationCustomUsername"
required
type="text"
/>
<c-form-feedback [valid]="false">Please choose a username.</c-form-feedback>
</c-input-group>
</c-col>
<c-col [md]="6">
<label cLabel for="validationCustom03">City</label>
<input cFormControl id="validationCustom03" required type="text" />
<c-form-feedback [valid]="false">Please provide a valid city.</c-form-feedback>
</c-col>
<c-col [md]="3">
<label cLabel for="validationCustom04">State</label>
<select cSelect id="validationCustom04" required>
<option value="">Choose...</option>
<option>...</option>
</select>
<c-form-feedback [valid]="false">Please provide a valid State.</c-form-feedback>
</c-col>
<c-col [md]="3">
<label cLabel for="validationCustom05">Zip code</label>
<input cFormControl id="validationCustom05" required type="text" />
<c-form-feedback [valid]="false">Please provide a valid zip.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<c-form-check>
<input cFormCheckInput id="invalidCheck" name="invalidCheck" required type="checkbox" />
<label cFormCheckLabel for="invalidCheck">Agree to terms and conditions</label>
</c-form-check>
<c-form-feedback [valid]="false">You must agree before submitting.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<button cButton class="me-1" color="primary" type="submit">
Submit form
</button>
<button (click)="onReset1()" cButton color="secondary" type="reset">
Reset
</button>
</c-col>
</form>
Browser defaults
Not interested in custom validation feedback messages or writing JavaScript to change form behaviors? All good, you
can use the browser defaults with ngNativeValidate. Try submitting the form below. Depending on your browser and OS,
you’ll see a slightly
different style of feedback.
While these feedback styles cannot be styled with CSS, you can still customize the feedback text through JavaScript.
import { Component, signal } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
ButtonDirective,
ColComponent,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormDirective,
FormFeedbackComponent,
FormLabelDirective,
FormSelectDirective,
GutterDirective,
InputGroupComponent,
InputGroupTextDirective,
RowDirective
} from '@coreui/angular';
@Component({
selector: 'docs-validation-browser-defaults',
templateUrl: './validation-browser-defaults.component.html',
imports: [
ReactiveFormsModule,
FormsModule,
FormDirective,
RowDirective,
GutterDirective,
ColComponent,
FormLabelDirective,
FormControlDirective,
FormFeedbackComponent,
InputGroupComponent,
InputGroupTextDirective,
FormSelectDirective,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
ButtonDirective
]
})
export class ValidationBrowserDefaultsComponent {
readonly browserDefaultsValidated = signal(false);
onSubmit2() {
this.browserDefaultsValidated.set(true);
console.log('Submit... 2');
}
onReset2() {
this.browserDefaultsValidated.set(false);
console.log('Reset... 3');
}
}<form #browserDefaultsForm="ngForm"
(ngSubmit)="onSubmit2()"
[gutter]="3"
[validated]="browserDefaultsValidated()"
cForm
cRow
ngNativeValidate
>
<c-col [md]="4">
<label cLabel for="validationDefault01">Email</label>
<input cFormControl id="validationDefault01" required type="text" value="Mark" />
<c-form-feedback [valid]="true">Looks good!</c-form-feedback>
</c-col>
<c-col [md]="4">
<label cLabel for="validationDefault02">Email</label>
<input cFormControl id="validationDefault02" required type="text" value="Otto" />
<c-form-feedback [valid]="true">Looks good!</c-form-feedback>
</c-col>
<c-col [md]="4">
<label cLabel for="validationDefaultUsername">Username</label>
<c-input-group class="has-validation">
<span cInputGroupText id="inputGroupPrepend1">@</span>
<input aria-describedby="inputGroupPrepend1"
cFormControl
id="validationDefaultUsername"
required
type="text"
/>
<c-form-feedback [valid]="false">Please choose a username.</c-form-feedback>
</c-input-group>
</c-col>
<c-col [md]="6">
<label cLabel for="validationDefault03">City</label>
<input cFormControl id="validationDefault03" required type="text" />
<c-form-feedback [valid]="false">Please provide a valid city.</c-form-feedback>
</c-col>
<c-col [md]="3">
<label cLabel for="validationDefault04">State</label>
<select cSelect id="validationDefault04" required>
<option value="">Choose...</option>
<option>...</option>
</select>
<c-form-feedback [valid]="false">Please provide a valid State.</c-form-feedback>
</c-col>
<c-col [md]="3">
<label cLabel for="validationDefault05">Zip code</label>
<input cFormControl id="validationDefault05" required type="text" />
<c-form-feedback [valid]="false">Please provide a valid zip.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<c-form-check>
<input cFormCheckInput id="invalidCheck1" name="invalidCheck" required type="checkbox" />
<label cFormCheckLabel for="invalidCheck1">Agree to terms and conditions</label>
</c-form-check>
<c-form-feedback [valid]="false">You must agree before submitting.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<button cButton class="me-1" color="primary" type="submit">
Submit form
</button>
<button (click)="onReset2()" cButton color="secondary" type="reset">
Reset
</button>
</c-col>
</form>Server side
We recommend using client-side validation, but in case you require server-side validation, you can indicate invalid
and valid form fields with valid boolean property.
For invalid fields, ensure that the invalid feedback/error message is associated with the relevant form field using
aria-describedby (noting that this attribute allows more than one id to be referenced, in case the field already
points to additional form text).
Input group needs an extra .has-validation class to fix border radius issues with c-form-feedback element.
import { Component } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
ButtonDirective,
ColComponent,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormDirective,
FormFeedbackComponent,
FormLabelDirective,
FormSelectDirective,
GutterDirective,
InputGroupComponent,
InputGroupTextDirective,
RowDirective
} from '@coreui/angular';
@Component({
selector: 'docs-validation-server-side',
templateUrl: './validation-server-side.component.html',
imports: [
ReactiveFormsModule,
FormsModule,
FormDirective,
RowDirective,
GutterDirective,
ColComponent,
FormLabelDirective,
FormControlDirective,
FormFeedbackComponent,
InputGroupComponent,
InputGroupTextDirective,
FormSelectDirective,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
ButtonDirective
]
})
export class ValidationServerSideComponent {}<form [gutter]="3" cForm cRow class="needs-validation" ngNativeValidate>
<c-col [md]="4">
<label cLabel for="validationServer01">First name</label>
<input [valid]="true"
cFormControl
id="validationServer01"
required
type="text"
value="Mark"
/>
<c-form-feedback [valid]="true">Looks good!</c-form-feedback>
</c-col>
<c-col [md]="4">
<label cLabel for="validationServer02">Last name</label>
<input [valid]="true"
cFormControl
id="validationServer02"
required
type="text"
value="Otto"
/>
<c-form-feedback [valid]="true">Looks good!</c-form-feedback>
</c-col>
<c-col [md]="4">
<label cLabel for="validationServerUsername">Username</label>
<c-input-group class="has-validation">
<span cInputGroupText id="inputGroupPrepend03">@</span>
<input [valid]="false"
aria-describedby="inputGroupPrepend03"
cFormControl
id="validationServerUsername"
required
type="text"
/>
<c-form-feedback [valid]="false">Please choose a username.</c-form-feedback>
</c-input-group>
</c-col>
<c-col [md]="6">
<label cLabel for="validationServer03">City</label>
<input [valid]="false" cFormControl id="validationServer03" required type="text" />
<c-form-feedback [valid]="false">Please provide a valid city.</c-form-feedback>
</c-col>
<c-col [md]="3">
<label cLabel for="validationServer04">State</label>
<select [valid]="false" cSelect id="validationServer04">
<option disabled>Choose...</option>
<option>...</option>
</select>
<c-form-feedback [valid]="false">Please provide a valid state.</c-form-feedback>
</c-col>
<c-col [md]="3">
<label cLabel for="validationServer05">Zip code</label>
<input [valid]="false" cFormControl id="validationServer05" required type="text" />
<c-form-feedback [valid]="false">Please provide a valid zip.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<c-form-check class="mb-3">
<input [valid]="false" cFormCheckInput id="invalidCheckServer" required type="checkbox">
<label cFormCheckLabel for="invalidCheckServer">Agree to terms and conditions</label>
</c-form-check>
<c-form-feedback [valid]="false">You must agree before submitting.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<button cButton color="primary" type="submit">
Submit form
</button>
</c-col>
</form>
Supported elements
Validation styles are available for the following form controls and components:
input cFormControlselect cSelectc-form-check
import { Component } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
ButtonDirective,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormDirective,
FormFeedbackComponent,
FormLabelDirective,
FormSelectDirective
} from '@coreui/angular';
@Component({
selector: 'docs-validation-supported-elements',
templateUrl: './validation-supported-elements.component.html',
imports: [
ReactiveFormsModule,
FormsModule,
FormDirective,
FormLabelDirective,
FormControlDirective,
FormFeedbackComponent,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormSelectDirective,
ButtonDirective
]
})
export class ValidationSupportedElementsComponent {}<form [validated]="true" cForm>
<div class="mb-3">
<label cLabel class="form-label" for="validationTextarea">
Textarea
</label>
<textarea [valid]="false"
cFormControl
id="validationTextarea"
placeholder="Required example textarea"
required
></textarea>
<c-form-feedback [valid]="false">Please enter a message in the textarea.</c-form-feedback>
</div>
<c-form-check class="mb-3">
<input cFormCheckInput id="validationFormCheck1" name="validationFormCheck1" required type="checkbox" />
<label cFormCheckLabel for="validationFormCheck1">Check this checkbox</label>
</c-form-check>
<c-form-feedback [valid]="false">Example invalid feedback text</c-form-feedback>
<c-form-check>
<input cFormCheckInput id="validationFormCheck2" name="radio-stacked" required type="radio" />
<label cFormCheckLabel for="validationFormCheck2">Check this radio</label>
</c-form-check>
<c-form-check class="mb-3">
<input cFormCheckInput id="validationFormCheck3" name="radio-stacked" required type="radio" />
<label cFormCheckLabel for="validationFormCheck3">Check this radio</label>
</c-form-check>
<c-form-feedback [valid]="false">More example invalid feedback text</c-form-feedback>
<div class="mb-3">
<select aria-label="select example" cSelect required>
<option value="">Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<c-form-feedback [valid]="false">Example invalid select feedback</c-form-feedback>
</div>
<div class="mb-3">
<input aria-label="file example"
cFormControl
id="validationText1"
required
type="file"
/>
<c-form-feedback [valid]="false">Example invalid form file feedback</c-form-feedback>
</div>
<div class="mb-3">
<button cButton color="primary" disabled type="submit">
Submit form
</button>
</div>
</form>
Tooltips
If your form layout allows it, you can swap the text for the tooltip to display validation feedback in a styled
tooltip. Be sure to have a parent with position: relative on it for tooltip positioning. In the example below, our
column classes have this already, but your project may require an alternative setup.
import { Component, signal } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
ButtonDirective,
ColComponent,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormDirective,
FormFeedbackComponent,
FormLabelDirective,
FormSelectDirective,
InputGroupComponent,
InputGroupTextDirective
} from '@coreui/angular';
@Component({
selector: 'docs-validation-tooltips',
templateUrl: './validation-tooltips.component.html',
imports: [
ReactiveFormsModule,
FormsModule,
FormDirective,
ColComponent,
FormLabelDirective,
FormControlDirective,
FormFeedbackComponent,
InputGroupComponent,
InputGroupTextDirective,
FormSelectDirective,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
ButtonDirective
]
})
export class ValidationTooltipsComponent {
readonly tooltipValidated = signal(false);
onSubmit3() {
this.tooltipValidated.set(true);
console.log('Submit... 3');
}
onReset3() {
this.tooltipValidated.set(false);
console.log('Reset... 3');
}
}<form #tooltipForm="ngForm"
(ngSubmit)="onSubmit3()"
[validated]="tooltipValidated()"
cForm
class="row g-3 needs-validation"
>
<c-col class="position-relative" [md]="4">
<label cLabel for="validationTooltip01">First name</label>
<input cFormControl id="validationTooltip01" required type="text" value="Mark" />
<c-form-feedback [valid]="true" tooltip>Looks good!</c-form-feedback>
</c-col>
<c-col class="position-relative" [md]="4">
<label cLabel for="validationTooltip02">Last name</label>
<input cFormControl id="validationTooltip02" required type="text" value="Otto" />
<c-form-feedback [valid]="true" tooltip>Looks good!</c-form-feedback>
</c-col>
<c-col class="position-relative" [md]="4">
<label cLabel for="validationTooltipUsername">Username</label>
<c-input-group class="has-validation">
<span cInputGroupText id="inputGroupPrependTooltip">@</span>
<input aria-describedby="inputGroupPrependTooltip"
cFormControl
id="validationTooltipUsername"
required
type="text"
/>
<c-form-feedback [valid]="false" tooltip>Please choose a username.</c-form-feedback>
</c-input-group>
</c-col>
<c-col class="position-relative" [md]="6">
<label cLabel for="validationTooltip03">City</label>
<input cFormControl id="validationTooltip03" required type="text" />
<c-form-feedback [valid]="false" tooltip>Please provide a valid city.</c-form-feedback>
</c-col>
<c-col class="position-relative" [md]="3">
<label cLabel for="validationTooltip04">State</label>
<select cSelect id="validationTooltip04" required>
<option value="">Choose...</option>
<option value="1">...</option>
</select>
<c-form-feedback [valid]="false" tooltip>Please provide a valid State.</c-form-feedback>
</c-col>
<c-col class="position-relative" [md]="3">
<label cLabel for="validationTooltip05">Zip code</label>
<input cFormControl id="validationTooltip05" required type="text" />
<c-form-feedback [valid]="false" tooltip>Please provide a valid zip.</c-form-feedback>
</c-col>
<c-col class="position-relative" [xs]="12">
<c-form-check>
<input cFormCheckInput id="invalidCheckTooltip" name="invalidCheckTooltip" required type="checkbox" />
<label cFormCheckLabel for="invalidCheckTooltip">Agree to terms and conditions</label>
</c-form-check>
<c-form-feedback [valid]="false" tooltip>You must agree before submitting.</c-form-feedback>
</c-col>
<c-col [xs]="12">
<button cButton class="me-1" color="primary" type="submit">
Submit form
</button>
<button (click)="onReset3()" cButton color="secondary" type="reset">
Reset
</button>
</c-col>
</form>
Enhanced example
With Angular validators.
import { JsonPipe, NgClass } from '@angular/common';
import { Component, inject, signal } from '@angular/core';
import {
AbstractControl,
FormBuilder,
FormGroup,
ReactiveFormsModule,
ValidationErrors,
ValidatorFn,
Validators
} from '@angular/forms';
import {
ButtonDirective,
ButtonGroupComponent,
CardBodyComponent,
CardComponent,
ColComponent,
ColDirective,
DatePickerComponent,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormDirective,
FormFeedbackComponent,
FormLabelDirective,
FormPasswordDirective,
MultiSelectComponent,
MultiSelectOptionComponent,
RowComponent
} from '@coreui/angular';
import { ValidationFormsService } from './validation-forms.service';
/** passwords must match - custom validator */
export const passwordMismatchValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const password = control.get('password');
const confirm = control.get('confirmPassword');
return password && confirm && password.value !== confirm.value ? { passwordMismatch: true } : null;
};
@Component({
selector: 'docs-validation-enhanced',
templateUrl: './validation-enhanced.component.html',
styleUrls: ['./validation-enhanced.component.scss'],
providers: [ValidationFormsService],
imports: [
ButtonDirective,
ButtonGroupComponent,
CardBodyComponent,
CardComponent,
ColComponent,
ColDirective,
DatePickerComponent,
FormDirective,
FormCheckComponent,
FormCheckInputDirective,
FormCheckLabelDirective,
FormControlDirective,
FormFeedbackComponent,
FormLabelDirective,
FormPasswordDirective,
JsonPipe,
MultiSelectComponent,
MultiSelectOptionComponent,
NgClass,
ReactiveFormsModule,
RowComponent
]
})
export class ValidationEnhancedComponent {
readonly formBuilder = inject(FormBuilder);
readonly validationFormsService = inject(ValidationFormsService);
readonly submitted = signal(false);
simpleForm!: FormGroup;
formErrors = this.validationFormsService.errorMessages;
formControls!: string[];
constructor() {
this.createForm();
}
createForm() {
this.simpleForm = this.formBuilder.group(
{
firstName: ['', [Validators.required]],
lastName: ['', [Validators.required]],
username: [
'',
[
Validators.required,
Validators.minLength(this.validationFormsService.formRules.usernameMin),
Validators.pattern(this.validationFormsService.formRules.nonEmpty)
]
],
email: ['', [Validators.required, Validators.email]],
password: [
'',
[
Validators.required,
Validators.minLength(this.validationFormsService.formRules.passwordMin),
Validators.pattern(this.validationFormsService.formRules.passwordPattern)
]
],
confirmPassword: [
'',
[
Validators.required,
Validators.minLength(this.validationFormsService.formRules.passwordMin),
Validators.pattern(this.validationFormsService.formRules.passwordPattern)
]
],
birthday: [null as Date | null, [Validators.required]],
framework: ['', [Validators.required]],
accept: [false, [Validators.requiredTrue]]
},
{ validators: passwordMismatchValidator }
);
this.formControls = Object.keys(this.simpleForm.controls);
}
onReset() {
this.submitted.set(false);
this.simpleForm.reset();
}
onValidate() {
this.submitted.set(true);
// stop here if form is invalid
return this.simpleForm.status === 'VALID';
}
onSubmit() {
console.warn(this.onValidate(), this.simpleForm.value);
if (this.onValidate()) {
// TODO: Submit form value
console.warn(this.simpleForm.value);
alert('SUCCESS!');
}
}
isValid(ctrl: AbstractControl): boolean | undefined {
return ctrl.touched && ctrl.valid ? true : (this.submitted() || ctrl.touched) && ctrl.invalid ? false : undefined;
}
}<c-row>
<c-col [md]="6">
<form (ngSubmit)="onSubmit()" [formGroup]="simpleForm" cForm novalidate>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="firstName">First name</label>
@if (simpleForm.controls['firstName']; as ctrl) {
<c-col [sm]="8">
<input
[valid]="isValid(ctrl)"
autocomplete="off"
cFormControl
formControlName="firstName"
id="firstName"
placeholder="First name"
required
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>First Name is required</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="lastName">Last name</label>
@if (simpleForm.controls['lastName']; as ctrl) {
<c-col [sm]="8">
<input
[valid]="isValid(ctrl)"
autocomplete="off"
cFormControl
formControlName="lastName"
id="lastName"
placeholder="Last name"
required
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Last Name is required</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="username">Username</label>
@if (simpleForm.controls['username']; as ctrl) {
<c-col [sm]="8">
<input
[valid]="isValid(ctrl)"
autocomplete="off"
cFormControl
formControlName="username"
id="username"
placeholder="Username"
required
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Username is required</div>
} @else if (ctrl.hasError('minlength')) {
<div>
{{ formErrors['username'].minLength }}
</div>
} @else if (ctrl.hasError('pattern')) {
<div>
{{ formErrors['username'].pattern }}
</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="email">Email</label>
@if (simpleForm.controls['email']; as ctrl) {
<c-col [sm]="8">
<input
[valid]="isValid(ctrl)"
autocomplete="off"
cFormControl
formControlName="email"
id="email"
placeholder="Email"
required
type="email"
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Email is required</div>
} @else if (ctrl.hasError('email')) {
<div>
{{ formErrors['email']?.email }}
</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="password">Password</label>
@if (simpleForm.controls['password']; as ctrl) {
<c-col [sm]="8">
<input
[valid]="isValid(ctrl)"
autocomplete="new-password"
cFormPassword
formControlName="password"
id="password"
placeholder="Password"
required
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Password is required</div>
} @else if (ctrl.hasError('minlength')) {
<div>{{ formErrors['password'].minLength }}</div>
} @else if (ctrl.hasError('pattern')) {
<div>{{ formErrors['password'].pattern }}</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="confirmPassword">Confirm password</label>
@if (simpleForm.controls['confirmPassword']; as ctrl) {
<c-col [sm]="8">
<input
[valid]="isValid(ctrl)"
autocomplete="off"
cFormPassword
formControlName="confirmPassword"
id="confirmPassword"
placeholder="Confirm password"
required
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Confirmation is required</div>
} @else if (simpleForm.hasError('passwordMismatch')) {
<div>
{{ formErrors['confirmPassword'].passwordMismatch }}
</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="birthday">Date of Birth</label>
@if (simpleForm.controls['birthday']; as ctrl) {
<c-col [sm]="8">
<c-date-picker
[valid]="(!ctrl.untouched && ctrl.valid) ? true : (submitted() || !ctrl.untouched) && ctrl.invalid ? false : undefined"
formControlName="birthday"
id="birthday"
required
/>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Birthday date required</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
<label [sm]="4" cCol cLabel="col" for="framework">Framework</label>
@if (simpleForm.controls['framework']; as ctrl) {
<c-col [sm]="8">
<c-multi-select
[valid]="(ctrl.touched && ctrl.valid) ? true : (submitted() || ctrl.touched) && ctrl.invalid ? false : undefined"
formControlName="framework"
id="framework"
required
>
<c-multi-select-option>Angular</c-multi-select-option>
<c-multi-select-option>Bootstrap</c-multi-select-option>
<c-multi-select-option>React.js</c-multi-select-option>
<c-multi-select-option>Vue.js</c-multi-select-option>
</c-multi-select>
@if (submitted() || ctrl.invalid) {
<c-form-feedback [valid]="!(submitted() || ctrl.invalid)">
@if (ctrl.hasError('required')) {
<div>Framework is required</div>
}
</c-form-feedback>
}
</c-col>
}
</c-row>
<c-row class="mb-1">
@if (simpleForm.controls['accept']; as ctrl) {
<c-col>
<c-form-check>
<input
[valid]="isValid(ctrl)"
cFormCheckInput
formControlName="accept"
id="accept"
required
type="checkbox"
/>
<label cFormCheckLabel for="accept">I accept the terms of use</label>
<c-form-feedback [valid]="false">
@if (ctrl.hasError('required')) {
<div>You have to accept our Terms and Conditions</div>
}
</c-form-feedback>
</c-form-check>
</c-col>
}
</c-row>
<hr />
<c-button-group>
<button
[disabled]="simpleForm.pristine || simpleForm.invalid"
[tabindex]="'0'"
cButton
color="primary"
type="submit"
>
Submit
</button>
<button
(click)="onValidate()"
[disabled]="simpleForm.valid"
[tabindex]="'0'"
cButton
color="success"
>
Validate
</button>
<button (click)="onReset()" [tabindex]="'0'" cButton color="danger" type="reset">
Reset
</button>
</c-button-group>
</form>
</c-col>
<c-col [md]="6">
<c-card [ngClass]="{ 'bg-info': simpleForm.valid, 'bg-dark': simpleForm.invalid}">
<c-card-body class="text-white">
<pre>Value: <code>{{ simpleForm.value | json }}</code></pre>
<ul>
<li>Status: {{ simpleForm.status }}</li>
<li>Valid: {{ simpleForm.valid }}</li>
<li>Pristine: {{ simpleForm.pristine }}</li>
<li>Errors: {{ simpleForm.errors | json }}</li>
</ul>
<ul>
@for (ctrl of formControls; track ctrl) {
<li>
{{ ctrl }}
<ul>
<li>Invalid: {{ simpleForm.controls[ctrl].invalid | json }}</li>
<li>Pristine: {{ simpleForm.controls[ctrl].pristine | json }}</li>
<li>Status: {{ simpleForm.controls[ctrl].status | json }}</li>
<li>Touched: {{ simpleForm.controls[ctrl].touched | json }}</li>
<li>Errors: {{ simpleForm.controls[ctrl].errors | json }}</li>
</ul>
</li>
}
</ul>
</c-card-body>
</c-card>
</c-col>
</c-row>.btn:disabled {
cursor: auto;
}
input[autocomplete='off']::-webkit-contacts-auto-fill-button,
input[autocomplete='off']::-webkit-credentials-auto-fill-button {
visibility: hidden;
position: absolute;
right: 0;
}import { Injectable } from '@angular/core';
@Injectable()
export class ValidationFormsService {
errorMessages: Record<string, any>;
formRules = {
nonEmpty: '^[a-zA-Z0-9]+([_ -]?[a-zA-Z0-9])*$',
usernameMin: 5,
passwordMin: 6,
passwordPattern: '(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,}'
};
formErrors = {
firstName: '',
lastName: '',
username: '',
email: '',
password: '',
confirmPassword: '',
birthday: '',
framework: '',
accept: false
};
constructor() {
this.errorMessages = {
firstName: {
required: 'First name is required'
},
lastName: {
required: 'Last name is required'
},
username: {
required: 'Username is required',
minLength: `Username must be ${this.formRules.usernameMin} characters or more`,
pattern: 'Must contain letters and/or numbers, no trailing spaces'
},
email: {
required: 'required',
email: 'Invalid email address'
},
password: {
required: 'Password is required',
pattern: 'Password must contain: numbers, uppercase and lowercase letters',
minLength: `Password must be at least ${this.formRules.passwordMin} characters`
},
confirmPassword: {
required: 'Password confirmation is required',
passwordMismatch: 'Passwords must match'
},
birthday: {
required: 'Birthday date required'
},
framework: {
required: 'Framework is required'
},
accept: {
requiredTrue: 'You have to accept our Terms and Conditions'
}
};
}
}