// app/custom-validation/customer-request.ts
export interface CustomerRequest {
id: number;
customerId: string;
date: Date;
message: string;
}
// app/custom-validation/not-in-year-validator.directive.ts
import { Directive, Input } from '@angular/core';
import {
AbstractControl,
NG_VALIDATORS,
ValidationErrors,
Validator,
ValidatorFn,
} from '@angular/forms';
export function notInYearValidator(notInYear: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.value) {
return null;
}
const date = control.value as Date;
return date.getFullYear() == notInYear
? { notInYear: { value: control.value } }
: null;
};
}
// IMPORTANT: The directive is not needed if you're using reactive forms
@Directive({
selector: '[appNotInYear]',
providers: [
{
provide: NG_VALIDATORS,
useExisting: NotInYearValidatorDirective,
multi: true,
},
],
})
export class NotInYearValidatorDirective implements Validator {
@Input('appNotInYear')
notInYear?: number;
validate(control: AbstractControl): ValidationErrors | null {
return this.notInYear ? notInYearValidator(this.notInYear)(control) : null;
}
}
// app/custom-validation/valid-customer-id-validator.directive.ts
import { Directive } from '@angular/core';
import {
AbstractControl,
NG_VALIDATORS,
ValidationErrors,
Validator,
ValidatorFn,
} from '@angular/forms';
export function validCustomerIdValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.value) {
return null;
}
const valid = /^[A-Z]{2}[0-9]{4}$/.test(control.value);
return valid ? null : { validCustomerId: { value: control.value } };
};
}
// IMPORTANT: The directive is not needed if you're using reactive forms
@Directive({
selector: '[appValidCustomerId]',
providers: [
{
provide: NG_VALIDATORS,
useExisting: ValidCustomerIdValidatorDirective,
multi: true,
},
],
})
export class ValidCustomerIdValidatorDirective implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
return validCustomerIdValidator()(control);
}
}
<!-- app/custom-validation/customer-request-form-reactive/customer-request-form-reactive.component.html -->
<h1>Customer Request (reactive)</h1>
<mat-card>
<form [formGroup]="customerRequestForm" (ngSubmit)="submit()">
<mat-form-field>
<mat-label>Customer ID</mat-label>
<input matInput name="customerId" formControlName="customerId" />
<mat-error *ngIf="customerId?.errors?.required"
>You must enter a value</mat-error
>
<mat-error *ngIf="customerId?.errors?.validCustomerId"
>The ID must start with two uppercase letters followed by four
numbers</mat-error
>
</mat-form-field>
<mat-form-field>
<mat-label>Date</mat-label>
<input
matInput
name="date"
formControlName="date"
[matDatepicker]="picker"
/>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="date?.errors?.required"
>You must enter a value</mat-error
>
<mat-error *ngIf="date?.errors?.notInYear"
>The date cannot be in 2020</mat-error
>
</mat-form-field>
<mat-form-field>
<mat-label>Message</mat-label>
<textarea matInput name="message" formControlName="message"></textarea>
<mat-error *ngIf="message?.errors?.required"
>You must enter a value</mat-error
>
</mat-form-field>
<div class="form-buttons">
<button
mat-raised-button
type="submit"
color="primary"
[disabled]="loading || !customerRequestForm.valid"
>
Submit
</button>
</div>
</form>
</mat-card>
<h2>Customer Request (JSON)</h2>
<pre
>{{ customerRequestForm.value | json }}
</pre>
// app/custom-validation/customer-request-form-reactive/customer-request-form-reactive.component.ts
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { finalize } from 'rxjs/operators';
import { CustomerRequest } from 'src/app/custom-validation/customer-request';
import { CustomerRequestService } from 'src/app/custom-validation/customer-request.service';
import { notInYearValidator } from 'src/app/custom-validation/not-in-year-validator.directive';
import { validCustomerIdValidator } from 'src/app/custom-validation/valid-customer-id-validator.directive';
@Component({
selector: 'app-customer-request-form-reactive',
templateUrl: './customer-request-form-reactive.component.html',
})
export class CustomerRequestFormReactiveComponent {
readonly customerRequestForm = this.formBuilder.group({
customerId: ['', [Validators.required, validCustomerIdValidator()]],
date: ['', [Validators.required, notInYearValidator(2020)]],
message: ['', Validators.required],
});
loading = false;
get customerId() {
return this.customerRequestForm.get('customerId');
}
get date() {
return this.customerRequestForm.get('date');
}
get message() {
return this.customerRequestForm.get('message');
}
constructor(
private readonly formBuilder: FormBuilder,
private readonly customerRequestService: CustomerRequestService,
private readonly snackBar: MatSnackBar
) {}
submit(): void {
const customerRequest: CustomerRequest = this.customerRequestForm.value;
this.loading = true;
this.customerRequestService
.save(customerRequest)
.pipe(finalize(() => (this.loading = false)))
.subscribe(({ id }) => {
this.snackBar.open(`Customer request saved with ID #${id}`, 'Close');
});
}
}
// app/custom-validation/customer-request.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { CustomerRequest } from 'src/app/custom-validation/customer-request';
export interface CustomerRequestSaveResponse {
id: number;
}
@Injectable({
providedIn: 'root',
})
export class CustomerRequestService {
save(
customerRequest: CustomerRequest
): Observable<CustomerRequestSaveResponse> {
console.log('Save:', customerRequest);
return of({
id: 1,
}).pipe(delay(1000));
}
}